123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- import { beHidden, beVisible, haveText, beChecked, haveAttribute, haveClasses, haveValue, notBeChecked, notHaveAttribute, notHaveClasses, test, html } from '../../utils'
- test('sets attribute bindings on initialize',
- html`
- <div x-data="{ foo: 'bar' }">
- <span x-ref="me" x-bind:foo="foo">[Subject]</span>
- </div>
- `,
- ({ get }) => get('span').should(haveAttribute('foo', 'bar'))
- )
- test('sets undefined nested keys to empty string',
- html`
- <div x-data="{ nested: {} }">
- <span x-bind:foo="nested.field">
- </div>
- `,
- ({ get }) => get('span').should(haveAttribute('foo', ''))
- )
- test('style attribute bindings are added by string syntax',
- html`
- <div x-data="{ initialClass: 'foo' }">
- <span x-bind:class="initialClass"></span>
- </div>
- `,
- ({ get }) => get('span').should(haveClasses(['foo']))
- )
- test('aria-pressed/checked attribute boolean values are cast to a true/false string',
- html`
- <div x-data="{ open: true }">
- <span x-bind:aria-pressed="open"></span>
- </div>
- `,
- ({ get }) => get('span').should(haveAttribute('aria-pressed', 'true'))
- )
- test('non-boolean attributes set to null/undefined/false are removed from the element',
- html`
- <div x-data="{}">
- <a href="#hello" x-bind:href="null">null</a>
- <a href="#hello" x-bind:href="false">false</a>
- <a href="#hello" x-bind:href="undefined">undefined</a>
- <!-- custom attribute see https://github.com/alpinejs/alpine/issues/280 -->
- <span visible="true" x-bind:visible="null">null</span>
- <span visible="true" x-bind:visible="false">false</span>
- <span visible="true" x-bind:visible="undefined">undefined</span>
- </div>
- `,
- ({ get }) => {
- get('a:nth-child(1)').should(notHaveAttribute('href'))
- get('a:nth-child(2)').should(notHaveAttribute('href'))
- get('a:nth-child(3)').should(notHaveAttribute('href'))
- get('span:nth-child(1)').should(notHaveAttribute('visible'))
- get('span:nth-child(2)').should(notHaveAttribute('visible'))
- get('span:nth-child(3)').should(notHaveAttribute('visible'))
- }
- )
- test('non-boolean empty string attributes are not removed',
- html`
- <div x-data>
- <a href="#hello" x-bind:href="''"></a>
- </div>
- `,
- ({ get }) => get('a').should(haveAttribute('href', ''))
- )
- test('boolean attribute values are set to their attribute name if true and removed if false',
- html`
- <div x-data="{ isSet: true }">
- <span @click="isSet = false" id="setToFalse">Set To False</span>
- <input x-bind:disabled="isSet"></input>
- <input x-bind:checked="isSet"></input>
- <input x-bind:required="isSet"></input>
- <input x-bind:readonly="isSet"></input>
- <details x-bind:open="isSet"></details>
- <select x-bind:multiple="isSet"></select>
- <option x-bind:selected="isSet"></option>
- <textarea x-bind:autofocus="isSet"></textarea>
- <dl x-bind:itemscope="isSet"></dl>
- <form x-bind:novalidate="isSet"></form>
- <iframe
- x-bind:allowfullscreen="isSet"
- x-bind:allowpaymentrequest="isSet"
- ></iframe>
- <button x-bind:formnovalidate="isSet"></button>
- <audio
- x-bind:autoplay="isSet"
- x-bind:controls="isSet"
- x-bind:loop="isSet"
- x-bind:muted="isSet"
- ></audio>
- <video x-bind:playsinline="isSet"></video>
- <track x-bind:default="isSet" />
- <img x-bind:ismap="isSet" />
- <ol x-bind:reversed="isSet"></ol>
- </div>
- `,
- ({ get }) => {
- get('input:nth-of-type(1)').should(haveAttribute('disabled', 'disabled'))
- get('input:nth-of-type(2)').should(haveAttribute('checked', 'checked'))
- get('input:nth-of-type(3)').should(haveAttribute('required', 'required'))
- get('input:nth-of-type(4)').should(haveAttribute('readonly', 'readonly'))
- get('details').should(haveAttribute('open', 'open'))
- get('select').should(haveAttribute('multiple', 'multiple'))
- get('option').should(haveAttribute('selected', 'selected'))
- get('textarea').should(haveAttribute('autofocus', 'autofocus'))
- get('dl').should(haveAttribute('itemscope', 'itemscope'))
- get('form').should(haveAttribute('novalidate', 'novalidate'))
- get('iframe').should(haveAttribute('allowfullscreen', 'allowfullscreen'))
- get('iframe').should(haveAttribute('allowpaymentrequest', 'allowpaymentrequest'))
- get('button').should(haveAttribute('formnovalidate', 'formnovalidate'))
- get('audio').should(haveAttribute('autoplay', 'autoplay'))
- get('audio').should(haveAttribute('controls', 'controls'))
- get('audio').should(haveAttribute('loop', 'loop'))
- get('audio').should(haveAttribute('muted', 'muted'))
- get('video').should(haveAttribute('playsinline', 'playsinline'))
- get('track').should(haveAttribute('default', 'default'))
- get('img').should(haveAttribute('ismap', 'ismap'))
- get('ol').should(haveAttribute('reversed', 'reversed'))
- get('#setToFalse').click()
- get('input:nth-of-type(1)').should(notHaveAttribute('disabled'))
- get('input:nth-of-type(2)').should(notHaveAttribute('checked'))
- get('input:nth-of-type(3)').should(notHaveAttribute('required'))
- get('input:nth-of-type(4)').should(notHaveAttribute('readonly'))
- get('details').should(notHaveAttribute('open'))
- get('select').should(notHaveAttribute('multiple'))
- get('option').should(notHaveAttribute('selected'))
- get('textarea').should(notHaveAttribute('autofocus'))
- get('dl').should(notHaveAttribute('itemscope'))
- get('form').should(notHaveAttribute('novalidate'))
- get('iframe').should(notHaveAttribute('allowfullscreen'))
- get('iframe').should(notHaveAttribute('allowpaymentrequest'))
- get('button').should(notHaveAttribute('formnovalidate'))
- get('audio').should(notHaveAttribute('autoplay'))
- get('audio').should(notHaveAttribute('controls'))
- get('audio').should(notHaveAttribute('loop'))
- get('audio').should(notHaveAttribute('muted'))
- get('video').should(notHaveAttribute('playsinline'))
- get('track').should(notHaveAttribute('default'))
- get('img').should(notHaveAttribute('ismap'))
- get('ol').should(notHaveAttribute('reversed'))
- get('script').should(notHaveAttribute('async'))
- get('script').should(notHaveAttribute('defer'))
- get('script').should(notHaveAttribute('nomodule'))
- }
- )
- test('boolean empty string attributes are not removed',
- html`
- <div x-data="{}">
- <input x-bind:disabled="''">
- </div>
- `,
- ({ get }) => get('input').should(haveAttribute('disabled', 'disabled'))
- )
- test('binding supports short syntax',
- html`
- <div x-data="{ foo: 'bar' }">
- <span :class="foo"></span>
- </div>
- `,
- ({ get }) => get('span').should(haveClasses(['bar']))
- )
- test('checkbox is unchecked by default',
- html`
- <div x-data="{foo: {bar: 'baz'}}">
- <input type="checkbox" x-bind:value="''"></input>
- <input type="checkbox" x-bind:value="'test'"></input>
- <input type="checkbox" x-bind:value="foo.bar"></input>
- <input type="checkbox" x-bind:value="0"></input>
- <input type="checkbox" x-bind:value="10"></input>
- </div>
- `,
- ({ get }) => {
- get('input:nth-of-type(1)').should(notBeChecked())
- get('input:nth-of-type(2)').should(notBeChecked())
- get('input:nth-of-type(3)').should(notBeChecked())
- get('input:nth-of-type(4)').should(notBeChecked())
- get('input:nth-of-type(5)').should(notBeChecked())
- }
- )
- test('radio is unchecked by default',
- html`
- <div x-data="{foo: {bar: 'baz'}}">
- <input type="radio" x-bind:value="''"></input>
- <input type="radio" x-bind:value="'test'"></input>
- <input type="radio" x-bind:value="foo.bar"></input>
- <input type="radio" x-bind:value="0"></input>
- <input type="radio" x-bind:value="10"></input>
- </div>
- `,
- ({ get }) => {
- get('input:nth-of-type(1)').should(notBeChecked())
- get('input:nth-of-type(2)').should(notBeChecked())
- get('input:nth-of-type(3)').should(notBeChecked())
- get('input:nth-of-type(4)').should(notBeChecked())
- get('input:nth-of-type(5)').should(notBeChecked())
- }
- )
- test('checkbox values are set correctly',
- html`
- <div x-data="{ stringValue: 'foo', trueValue: true, falseValue: false }">
- <input type="checkbox" name="stringCheckbox" :value="stringValue" />
- <input type="checkbox" name="trueCheckbox" :value="trueValue" />
- <input type="checkbox" name="falseCheckbox" :value="falseValue" />
- </div>
- `,
- ({ get }) => {
- get('input:nth-of-type(1)').should(haveValue('foo'))
- get('input:nth-of-type(2)').should(haveValue('on'))
- get('input:nth-of-type(3)').should(haveValue('on'))
- }
- )
- test('radio values are set correctly',
- html`
- <div x-data="{lists: [{id: 1}, {id: 8}], selectedListID: '8'}">
- <template x-for="list in lists" :key="list.id">
- <input x-model="selectedListID" type="radio" :value="list.id.toString()" :id="'list-' + list.id">
- </template>
- <input type="radio" id="list-test" value="test" x-model="selectedListID">
- </div>
- `,
- ({ get }) => {
- get('#list-1').should(haveValue('1'))
- get('#list-1').should(notBeChecked())
- get('#list-8').should(haveValue('8'))
- get('#list-8').should(beChecked())
- get('#list-test').should(haveValue('test'))
- get('#list-test').should(notBeChecked())
- }
- )
- test('.camel modifier correctly sets name of attribute',
- html`
- <div x-data>
- <svg x-bind:view-box.camel="'0 0 42 42'"></svg>
- </div>
- `,
- ({ get }) => get('svg').should(haveAttribute('viewBox', '0 0 42 42'))
- )
- test('attribute binding names can contain numbers',
- html`
- <svg x-data>
- <line x1="1" y1="2" :x2="3" x-bind:y2="4" />
- </svg>
- `,
- ({ get }) => {
- get('line').should(haveAttribute('x2', '3'))
- get('line').should(haveAttribute('y2', '4'))
- }
- )
- test('non-string and non-boolean attributes are cast to string when bound to checkbox',
- html`
- <div x-data="{ number: 100, zero: 0, bool: true, nullProp: null }">
- <input type="checkbox" id="number" :value="number">
- <input type="checkbox" id="zero" :value="zero">
- <input type="checkbox" id="boolean" :value="bool">
- <input type="checkbox" id="null" :value="nullProp">
- </div>
- `,
- ({ get }) => {
- get('input:nth-of-type(1)').should(haveValue('100'))
- get('input:nth-of-type(2)').should(haveValue('0'))
- get('input:nth-of-type(3)').should(haveValue('on'))
- get('input:nth-of-type(4)').should(haveValue('on'))
- }
- )
- test('can bind an object of directives',
- html`
- <script>
- window.modal = function () {
- return {
- foo: 'bar',
- trigger: {
- ['x-on:click']() { this.foo = 'baz' },
- },
- dialogue: {
- ['x-text']() { return this.foo },
- },
- }
- }
- </script>
- <div x-data="window.modal()">
- <button x-bind="trigger">Toggle</button>
- <span x-bind="dialogue">Modal Body</span>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText('bar'))
- get('button').click()
- get('span').should(haveText('baz'))
- }
- )
- test('x-bind object syntax supports normal HTML attributes',
- html`
- <span x-data x-bind="{ foo: 'bar' }"></span>
- `,
- ({ get }) => {
- get('span').should(haveAttribute('foo', 'bar'))
- }
- )
- test('x-bind object syntax supports x-for',
- html`
- <script>
- window.todos = () => { return {
- todos: ['foo', 'bar'],
- outputForExpression: {
- ['x-for']: 'todo in todos',
- }
- }}
- </script>
- <div x-data="window.todos()">
- <ul>
- <template x-bind="outputForExpression">
- <li x-text="todo"></li>
- </template>
- </ul>
- </div>
- `,
- ({ get }) => {
- get('li:nth-of-type(1)').should(haveText('foo'))
- get('li:nth-of-type(2)').should(haveText('bar'))
- }
- )
- test('x-bind object syntax syntax supports x-transition',
- html`
- <style>
- .transition { transition-property: background-color, border-color, color, fill, stroke; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; }
- .duration-100 { transition-duration: 100ms; }
- </style>
- <script>
- window.transitions = () => { return {
- show: true,
- outputClickExpression: {
- ['@click']() { this.show = false },
- ['x-text']() { return 'Click Me' },
- },
- outputTransitionExpression: {
- ['x-show']() { return this.show },
- ['x-transition:enter']: 'transition duration-100',
- ['x-transition:leave']: 'transition duration-100',
- },
- }}
- </script>
- <div x-data="transitions()">
- <button x-bind="outputClickExpression"></button>
- <span x-bind="outputTransitionExpression">thing</span>
- </div>
- `,
- ({ get }) => {
- get('span').should(beVisible())
- get('button').click()
- get('span').should(beVisible())
- get('span').should(beHidden())
- }
- )
- test('x-bind object syntax event handlers defined as functions receive the event object as their first argument',
- html`
- <script>
- window.data = () => { return {
- button: {
- ['@click']() {
- this.$refs.span.innerText = this.$el.id
- }
- }
- }}
- </script>
- <div x-data="window.data()">
- <button x-bind="button" id="bar">click me</button>
- <span x-ref="span">foo</span>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText('foo'))
- get('button').click()
- get('span').should(haveText('bar'))
- }
- )
|