123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- import { beVisible, haveLength, haveText, html, notBeVisible, test } from '../../utils'
- test('renders loops with x-for',
- html`
- <div x-data="{ items: ['foo'] }">
- <button x-on:click="items = ['foo', 'bar']">click me</button>
- <template x-for="item in items">
- <span x-text="item"></span>
- </template>
- </div>
- `,
- ({ get }) => {
- get('span:nth-of-type(1)').should(haveText('foo'))
- get('span:nth-of-type(2)').should(notBeVisible())
- get('button').click()
- get('span:nth-of-type(1)').should(haveText('foo'))
- get('span:nth-of-type(2)').should(haveText('bar'))
- }
- )
- test('renders loops with x-for that have space or newline',
- html`
- <div x-data="{ items: ['foo'] }">
- <button x-on:click="items = ['foo', 'bar']">click me</button>
- <div x-bind:id="1">
- <template x-for="
- (
- item
- ) in items
- ">
- <span x-text="item"></span>
- </template>
- </div>
- <div x-bind:id="2">
- <template x-for=" (
- item,
- index
- ) in items
- ">
- <span x-text="item"></span>
- </template>
- </div>
- </div>
- `,
- ({ get }) => {
- get('#1 span:nth-of-type(1)').should(haveText('foo'))
- get('#1 span:nth-of-type(2)').should(notBeVisible())
- get('#2 span:nth-of-type(1)').should(haveText('foo'))
- get('#2 span:nth-of-type(2)').should(notBeVisible())
- get('button').click()
- get('#1 span:nth-of-type(1)').should(haveText('foo'))
- get('#1 span:nth-of-type(2)').should(haveText('bar'))
- get('#2 span:nth-of-type(1)').should(haveText('foo'))
- get('#2 span:nth-of-type(2)').should(haveText('bar'))
- }
- )
- test('can destructure arrays',
- html`
- <div x-data="{ items: [[1, 'foo'], [2, 'bar']] }">
- <template x-for="[id, label] in items">
- <div x-bind:id="id">
- <span x-text="id"></span>
- <h1 x-text="label"></h1>
- </div>
- </template>
- </div>
- `,
- ({ get }) => {
- get('#1 span').should(haveText('1'))
- get('#1 h1').should(haveText('foo'))
- get('#2 span').should(haveText('2'))
- get('#2 h1').should(haveText('bar'))
- }
- )
- test.only('can destructure object',
- html`
- <div x-data="{ items: [{ foo: 'oof', bar: 'rab' }, { foo: 'ofo', bar: 'arb' }] }">
- <template x-for="({ foo, bar }, i) in items">
- <div x-bind:id="i + 1">
- <span x-text="foo"></span>
- <h1 x-text="bar"></h1>
- </div>
- </template>
- </div>
- `,
- ({ get }) => {
- get('#1 span').should(haveText('oof'))
- get('#1 h1').should(haveText('rab'))
- get('#2 span').should(haveText('ofo'))
- get('#2 h1').should(haveText('arb'))
- }
- )
- test('removes all elements when array is empty and previously had one item',
- html`
- <div x-data="{ items: ['foo'] }">
- <button x-on:click="items = []">click me</button>
- <template x-for="item in items">
- <span x-text="item"></span>
- </template>
- </div>
- `,
- ({ get }) => {
- get('span').should(beVisible())
- get('button').click()
- get('span').should(notBeVisible())
- }
- )
- test('removes all elements when array is empty and previously had multiple items',
- html`
- <div x-data="{ items: ['foo', 'bar', 'world'] }">
- <button x-on:click="items = []">click me</button>
- <template x-for="item in items">
- <span x-text="item"></span>
- </template>
- </div>
- `,
- ({ get }) => {
- get('span:nth-of-type(1)').should(beVisible())
- get('span:nth-of-type(2)').should(beVisible())
- get('span:nth-of-type(3)').should(beVisible())
- get('button').click()
- get('span:nth-of-type(1)').should(notBeVisible())
- get('span:nth-of-type(2)').should(notBeVisible())
- get('span:nth-of-type(3)').should(notBeVisible())
- }
- )
- test('elements inside of loop are reactive',
- html`
- <div x-data="{ items: ['first'], foo: 'bar' }">
- <button x-on:click="foo = 'baz'">click me</button>
- <template x-for="item in items">
- <span>
- <h1 x-text="item"></h1>
- <h2 x-text="foo"></h2>
- </span>
- </template>
- </div>
- `,
- ({ get }) => {
- get('span').should(beVisible())
- get('h1').should(haveText('first'))
- get('h2').should(haveText('bar'))
- get('button').click()
- get('span').should(beVisible())
- get('h1').should(haveText('first'))
- get('h2').should(haveText('baz'))
- }
- )
- test('components inside of loop are reactive',
- html`
- <div x-data="{ items: ['first'] }">
- <template x-for="item in items">
- <div x-data="{foo: 'bar'}" class="child">
- <span x-text="foo"></span>
- <button x-on:click="foo = 'bob'">click me</button>
- </div>
- </template>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText('bar'))
- get('button').click()
- get('span').should(haveText('bob'))
- }
- )
- test('components inside a plain element of loop are reactive',
- html`
- <div x-data="{ items: ['first'] }">
- <template x-for="item in items">
- <ul>
- <div x-data="{foo: 'bar'}" class="child">
- <span x-text="foo"></span>
- <button x-on:click="foo = 'bob'">click me</button>
- </div>
- </ul>
- </template>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText('bar'))
- get('button').click()
- get('span').should(haveText('bob'))
- }
- )
- test('adding key attribute moves dom nodes properly',
- html`
- <div x-data="{ items: ['foo', 'bar'] }">
- <button x-on:click="items = ['bob', 'bar', 'foo', 'baz']" id="reorder">click me</button>
- <button x-on:click="$el.parentElement.querySelectorAll('span').forEach((el, index) => el.og_loop_index = index)" id="assign">click me</button>
- <template x-for="item in items" :key="item">
- <span x-text="item"></span>
- </template>
- </div>
- `,
- ({ get }) => {
- let haveOgIndex = index => el => expect(el[0].og_loop_index).to.equal(index)
- get('#assign').click()
- get('span:nth-of-type(1)').should(haveOgIndex(0))
- get('span:nth-of-type(2)').should(haveOgIndex(1))
- get('#reorder').click()
- get('span:nth-of-type(1)').should(haveOgIndex(undefined))
- get('span:nth-of-type(2)').should(haveOgIndex(1))
- get('span:nth-of-type(3)').should(haveOgIndex(0))
- get('span:nth-of-type(4)').should(haveOgIndex(undefined))
- }
- )
- test('can key by index',
- html`
- <div x-data="{ items: ['foo', 'bar'] }">
- <button x-on:click="items = ['bar', 'foo', 'baz']" id="reorder">click me</button>
- <button x-on:click="$el.parentElement.querySelectorAll('span').forEach((el, index) => el.og_loop_index = index)" id="assign">click me</button>
- <template x-for="(item, index) in items" :key="index">
- <span x-text="item"></span>
- </template>
- </div>
- `,
- ({ get }) => {
- let haveOgIndex = index => el => expect(el[0].og_loop_index).to.equal(index)
- get('#assign').click()
- get('span:nth-of-type(1)').should(haveOgIndex(0))
- get('span:nth-of-type(2)').should(haveOgIndex(1))
- get('#reorder').click()
- get('span:nth-of-type(1)').should(haveOgIndex(0))
- get('span:nth-of-type(2)').should(haveOgIndex(1))
- get('span:nth-of-type(3)').should(haveOgIndex(undefined))
- }
- )
- test('can use index inside of loop',
- html`
- <div x-data="{ items: ['foo'] }">
- <template x-for="(item, index) in items">
- <div>
- <h1 x-text="items.indexOf(item)"></h1>
- <h2 x-text="index"></h2>
- </div>
- </template>
- </div>
- `,
- ({ get }) => {
- get('h1').should(haveText(0))
- get('h2').should(haveText(0))
- }
- )
- test('can use third iterator param (collection) inside of loop',
- html`
- <div x-data="{ items: ['foo'] }">
- <template x-for="(item, index, things) in items">
- <div>
- <h1 x-text="items"></h1>
- <h2 x-text="things"></h2>
- </div>
- </template>
- </div>
- `,
- ({ get }) => {
- get('h1').should(haveText('foo'))
- get('h2').should(haveText('foo'))
- }
- )
- test('listeners in loop get fresh iteration data even though they are only registered initially',
- html`
- <div x-data="{ items: ['foo'], output: '' }">
- <button x-on:click="items = ['bar']">click me</button>
- <template x-for="(item, index) in items">
- <span x-text="item" x-on:click="output = item"></span>
- </template>
- <h1 x-text="output"></h1>
- </div>
- `,
- ({ get }) => {
- get('h1').should(haveText(''))
- get('span').click()
- get('h1').should(haveText('foo'))
- get('button').click()
- get('span').click()
- get('h1').should(haveText('bar'))
- }
- )
- test('nested x-for',
- html`
- <div x-data="{ foos: [ {bars: ['bob', 'lob']} ] }">
- <button x-on:click="foos = [ {bars: ['bob', 'lob']}, {bars: ['law']} ]">click me</button>
- <template x-for="foo in foos">
- <h1>
- <template x-for="bar in foo.bars">
- <h2 x-text="bar"></h2>
- </template>
- </h1>
- </template>
- </div>
- `,
- ({ get }) => {
- get('h1:nth-of-type(1) h2:nth-of-type(1)').should(beVisible())
- get('h1:nth-of-type(1) h2:nth-of-type(2)').should(beVisible())
- get('h1:nth-of-type(2) h2:nth-of-type(1)').should(notBeVisible())
- get('button').click()
- get('h1:nth-of-type(1) h2:nth-of-type(1)').should(beVisible())
- get('h1:nth-of-type(1) h2:nth-of-type(2)').should(beVisible())
- get('h1:nth-of-type(2) h2:nth-of-type(1)').should(beVisible())
- }
- )
- test('x-for updates the right elements when new item are inserted at the beginning of the list',
- html`
- <div x-data="{ items: [{name: 'one', key: '1'}, {name: 'two', key: '2'}] }">
- <button x-on:click="items = [{name: 'zero', key: '0'}, {name: 'one', key: '1'}, {name: 'two', key: '2'}]">click me</button>
- <template x-for="item in items" :key="item.key">
- <span x-text="item.name"></span>
- </template>
- </div>
- `,
- ({ get }) => {
- get('span:nth-of-type(1)').should(haveText('one'))
- get('span:nth-of-type(2)').should(haveText('two'))
- get('button').click()
- get('span:nth-of-type(1)').should(haveText('zero'))
- get('span:nth-of-type(2)').should(haveText('one'))
- get('span:nth-of-type(3)').should(haveText('two'))
- }
- )
- test('nested x-for access outer loop variable',
- html`
- <div x-data="{ foos: [ {name: 'foo', bars: ['bob', 'lob']}, {name: 'baz', bars: ['bab', 'lab']} ] }">
- <template x-for="foo in foos">
- <h1>
- <template x-for="bar in foo.bars">
- <h2 x-text="foo.name+': '+bar"></h2>
- </template>
- </h1>
- </template>
- </div>
- `,
- ({ get }) => {
- get('h1:nth-of-type(1) h2:nth-of-type(1)').should(haveText('foo: bob'))
- get('h1:nth-of-type(1) h2:nth-of-type(2)').should(haveText('foo: lob'))
- get('h1:nth-of-type(2) h2:nth-of-type(1)').should(haveText('baz: bab'))
- get('h1:nth-of-type(2) h2:nth-of-type(2)').should(haveText('baz: lab'))
- }
- )
- test('sibling x-for do not interact with each other',
- html`
- <div x-data="{ foos: [1], bars: [1, 2] }">
- <template x-for="foo in foos">
- <h1 x-text="foo"></h1>
- </template>
- <template x-for="bar in bars">
- <h2 x-text="bar"></h2>
- </template>
- <button @click="foos = [1, 2];bars = [1, 2, 3]">Change</button>
- </div>
- `,
- ({ get }) => {
- get('h1:nth-of-type(1)').should(haveText('1'))
- get('h2:nth-of-type(1)').should(haveText('1'))
- get('h2:nth-of-type(2)').should(haveText('2'))
- get('button').click()
- get('h1:nth-of-type(1)').should(haveText('1'))
- get('h1:nth-of-type(2)').should(haveText('2'))
- get('h2:nth-of-type(1)').should(haveText('1'))
- get('h2:nth-of-type(2)').should(haveText('2'))
- get('h2:nth-of-type(3)').should(haveText('3'))
- }
- )
- test('x-for over range using i in x syntax',
- html`
- <div x-data>
- <template x-for="i in 10">
- <span x-text="i"></span>
- </template>
- </div>
- `,
- ({ get }) => get('span').should(haveLength('10'))
- )
- test('x-for over range using i in property syntax',
- html`
- <div x-data="{ count: 10 }">
- <template x-for="i in count">
- <span x-text="i"></span>
- </template>
- </div>
- `,
- ({ get }) => get('span').should(haveLength('10'))
- )
- test.retry(2)('x-for with an array of numbers',
- `
- <div x-data="{ items: [] }">
- <template x-for="i in items">
- <span x-text="i"></span>
- </template>
- <button @click="items.push(2)" id="first">click me</button>
- <button @click="items.push(3)" id="second">click me</button>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveLength('0'))
- get('#first').click()
- get('span').should(haveLength('1'))
- get('#second').click()
- get('span').should(haveLength('2'))
- }
- )
- test('x-for works with undefined',
- `
- <div x-data="{ items: undefined }">
- <template x-for="i in items">
- <span x-text="i"></span>
- </template>
- <button @click="items = [2]" id="first">click me</button>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveLength('0'))
- get('#first').click()
- get('span').should(haveLength('1'))
- }
- )
|