diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml new file mode 100644 index 00000000000..e34b48647e7 --- /dev/null +++ b/.github/workflows/tests-on-pr.yml @@ -0,0 +1,65 @@ +name: UI Tests + +on: + pull_request: + types: [opened, edited, synchronize ] + branches: + - 'dev' + paths: + - 'ui/**' + +jobs: + tests: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + browser: [chrome, firefox] + + name: Tests on ${{ matrix.browser }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 14 + + - name: Install dependencies + run: cd ./ui && npm i + + - name: Run tests in ${{ matrix.browser }} browser + uses: cypress-io/github-action@v2 + with: + browser: ${{ matrix.browser }} + command: npm run test:component:ci + working-directory: ui + tag: ${{ github.event_name }} # Tag will be either "push" or "pull_request" + group: Tests in ${{ matrix.browser }} browser + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} # Dashboard record key as an environment variable + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # To allow accurately detecting a build vs a re-run build + COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} # Either PR title or default information + + - name: Add workflow summary + env: + SUMMARY: | + Hi @${{ github.event.pull_request.user.login }} and thanks for the contribution! 👏 + + We've detected file changes inside the `ui` folder, so that triggered our Cypress component tests. + run: echo "$SUMMARY" >> $GITHUB_STEP_SUMMARY + if: always() + + - name: Output failure message + env: + FAILURE: ":bangbang: Seems like some of the tests are failing. Please amend 'em for someone from the team to be able to review it." + run: echo "$FAILURE" >> $GITHUB_STEP_SUMMARY + if: failure() || cancelled() + + - name: Output success message + env: + SUCCESS: ":white_check_mark: Seems like all the tests passed successfully, so your PR is ready to be reviewed!" + run: echo "$SUCCESS" >> $GITHUB_STEP_SUMMARY + if: success() \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 551c2f5a2c7..f3937d7199f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,8 @@ Hi! We are really excited that you are interested in contributing to Quasar 👏 - If you are resolving a special issue, add `(fix: #xxxx[,#xxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `fix: update entities encoding/decoding (fix #3899)`. - Provide detailed description of the bug in the PR. A live demo is preferred. +- Cypress e2e tests will run every time there is a code change in any file of the `ui` folder. + ## Development Setup You will need [Node.js](http://nodejs.org) **version 12.22.1+** along [Yarn](https://yarnpkg.com/) or [NPM](https://docs.npmjs.com/getting-started/installing-node). Read `package.json` and take notice of the scripts you can use. diff --git a/README.md b/README.md index abb335fb417..fb65cb02e99 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation) +[![UI Tests](https://github.com/quasarframework/quasar/actions/workflows/tests-on-pr.yml/badge.svg?branch=dev)](https://github.com/quasarframework/quasar/actions/workflows/tests-on-pr.yml) + Please submit a PR to https://github.com/quasarframework/quasar-awesome with your website/app/Quasar tutorial/video etc. Thank you! ## Supporting Quasar diff --git a/ui/dev/cypress.json b/ui/dev/cypress.json index cd50ea9c189..734bb3e324f 100644 --- a/ui/dev/cypress.json +++ b/ui/dev/cypress.json @@ -1,4 +1,5 @@ { + "projectId": "5zr217", "fixturesFolder": "../test/cypress/fixtures", "screenshotsFolder": "../test/cypress/screenshots", "pluginsFile": "../test/cypress/plugins/index.js", diff --git a/ui/package.json b/ui/package.json index 8969c771359..461e73a50b0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -30,7 +30,7 @@ "lint": "eslint --ext .js,.vue src dev", "lint-fix": "eslint --ext .js,.vue src dev --fix", "test:component": "cd ./dev && cypress open-ct && cd ..", - "test:component:ci": "cd ./dev && cypress run-ct && cd ..", + "test:component:ci": "cd ./dev && cypress run-ct --record --parallel && cd ..", "test:create": "node ./test/cypress/helpers/create-spec.js -c" }, "repository": { diff --git a/ui/src/components/menu/__tests__/QMenu.spec.js b/ui/src/components/menu/__tests__/QMenu.spec.js index 552fdc1ce1c..7b8973f55fc 100644 --- a/ui/src/components/menu/__tests__/QMenu.spec.js +++ b/ui/src/components/menu/__tests__/QMenu.spec.js @@ -15,7 +15,7 @@ describe('Menu API', () => { describe('(prop): touch-position', () => { it('should show menu at the position of the click', () => { mount(WrapperOne, { - attrs: { + props: { 'touch-position': true } }) @@ -71,7 +71,7 @@ describe('Menu API', () => { it('should not close the menu when clicking outside the menu when persistent', () => { mount(WrapperOne, { - attrs: { + props: { persistent: true } }) @@ -89,7 +89,7 @@ describe('Menu API', () => { it('should not close the menu when hitting the escape key when persistent', () => { mount(WrapperOne, { - attrs: { + props: { persistent: true } }) @@ -129,7 +129,7 @@ describe('Menu API', () => { it('should close the menu when clicking a menu child without v-close-popup when auto-close is true', () => { mount(WrapperOne, { - attrs: { + props: { 'auto-close': true } }) @@ -174,7 +174,7 @@ describe('Menu API', () => { it('should not switch focus back to parent element when closing if no-refocus is true', () => { mount(WrapperOne, { - attrs: { + props: { 'no-refocus': true } }) @@ -208,7 +208,7 @@ describe('Menu API', () => { it('should no switch focus to the menu when opening with no-focus is true', () => { mount(WrapperOne, { - attrs: { + props: { 'no-focus': true } }) @@ -226,7 +226,7 @@ describe('Menu API', () => { describe('(prop): fit', () => { it('should show a menu that matches the full with of the target when fit is supplied', () => { mount(WrapperOne, { - attrs: { + props: { target: '.other-target', fit: true } @@ -245,7 +245,7 @@ describe('Menu API', () => { it('should show a menu that not matches the full with of the target when fit is false', () => { mount(WrapperOne, { - attrs: { + props: { target: '.other-target', fit: false } @@ -266,7 +266,7 @@ describe('Menu API', () => { describe('(prop): cover', () => { it('should show a menu that overlays the target when using cover', () => { mount(WrapperOne, { - attrs: { + props: { cover: true } }) @@ -279,7 +279,7 @@ describe('Menu API', () => { it('should show a menu that overlays the target when using cover', () => { mount(WrapperOne, { - attrs: { + props: { cover: true, target: '.other-target' } @@ -293,7 +293,7 @@ describe('Menu API', () => { it('should ignore self property when using cover', () => { mount(WrapperOne, { - attrs: { + props: { cover: true, self: 'center right', target: '.other-target' @@ -328,7 +328,7 @@ describe('Menu API', () => { horizontalSelf.forEach((hS) => { it(`should position Anchor(${ vA } ${ hA }) & Self(${ vS } ${ hS }) correctly`, () => { mount(WrapperOne, { - attrs: { + props: { anchor: `${ vA } ${ hA }`, self: `${ vS } ${ hS }` } @@ -357,7 +357,7 @@ describe('Menu API', () => { verticalSelf.forEach((vS) => { it(`should offset vertical position Anchor(${ vA } left) & Self(${ vS } left) correctly`, () => { mount(WrapperOne, { - attrs: { + props: { anchor: `${ vA } left`, self: `${ vS } left`, offset: [ 0, 20 ] @@ -378,7 +378,7 @@ describe('Menu API', () => { horizontalSelf.forEach((hS) => { it(`should offset horizontal position Anchor(top ${ hA }) & Self(top ${ hS }) correctly`, () => { mount(WrapperOne, { - attrs: { + props: { anchor: `top ${ hA }`, self: `top ${ hS }`, offset: [ 20, 0 ] @@ -399,7 +399,7 @@ describe('Menu API', () => { describe('(prop): dark', () => { it('should set the --q-dark color as background and white text color', () => { mount(WrapperOne, { - attrs: { + props: { dark: true } }) @@ -414,7 +414,7 @@ describe('Menu API', () => { describe('(prop): square', () => { it('should not have border-radius when using this prop', () => { mount(WrapperOne, { - attrs: { + props: { square: true } }) @@ -429,7 +429,7 @@ describe('Menu API', () => { it('should specify a max-height when setting this prop', () => { const maxHeight = '30px' mount(WrapperOne, { - attrs: { + props: { maxHeight } }) @@ -444,7 +444,7 @@ describe('Menu API', () => { it('should specify a max-width when setting this prop', () => { const maxWidth = '30px' mount(WrapperOne, { - attrs: { + props: { maxWidth } }) @@ -470,7 +470,7 @@ describe('Menu API', () => { it('should emit @escape-key event when escape key is pressed', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onEscapeKey: fn } }) @@ -493,7 +493,7 @@ describe('Menu API', () => { it('should not emit @escape-key event when menu is persistent', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onEscapeKey: fn, persistent: true } @@ -520,7 +520,7 @@ describe('Menu API', () => { describe('(method): updatePosition', () => { it('should reposition the menu when it is no longer in correct position', () => { mount(WrapperTwo, { - attrs: { + props: { anchor: 'bottom left', self: 'bottom left' } @@ -564,7 +564,7 @@ describe('Menu API', () => { describe('(method): focus', () => { it('should focus the menu', () => { mount(WrapperOne, { - attrs: { + props: { 'no-focus': true } }) @@ -586,7 +586,7 @@ describe('Menu API', () => { it('should focus the autofocus element inside the menu', () => { mount(WrapperTwo, { - attrs: { + props: { 'no-focus': true } }) diff --git a/ui/src/components/select/QSelect.js b/ui/src/components/select/QSelect.js index 03b0f6853f8..555616087cd 100755 --- a/ui/src/components/select/QSelect.js +++ b/ui/src/components/select/QSelect.js @@ -127,7 +127,7 @@ export default createComponent({ emits: [ ...useFieldEmits, - 'add', 'remove', 'input-value', + 'add', 'remove', 'input-value', 'new-value', 'keyup', 'keypress', 'keydown', 'filter-abort' ], diff --git a/ui/src/components/select/__tests__/QSelect.spec.js b/ui/src/components/select/__tests__/QSelect.spec.js index 4550ae8ba69..43342b22841 100644 --- a/ui/src/components/select/__tests__/QSelect.spec.js +++ b/ui/src/components/select/__tests__/QSelect.spec.js @@ -1,7 +1,23 @@ /* eslint-disable no-unused-expressions */ import { mount } from '@cypress/vue' -import { ref, h } from 'vue' -import WrapperOne from './WrapperOne.vue' +import { h, ref } from 'vue' +import { vModelAdapter } from '../../../../test/cypress/helpers/v-model-adapter.js' +import QSelect from '../QSelect.js' + +function getHostElement (extendedSelector = '') { + return cy.get(`.q-select ${ extendedSelector }`) +} + +function mountQSelect (options = {}) { + if (!options.props?.modelValue) { + options.props = { + modelValue: null, + ...options.props ?? {} + } + } + + return mount(QSelect, options) +} // QSelect does not set the `data-cy` attribute on the root element, but on the `.q-field__native` element // This means we cannot use data-cy everywhere, but instead use a custom class `select-root` for this purpose @@ -49,12 +65,12 @@ describe('QSelect API', () => { describe('(prop): dropdown-icon', () => { it('should use the dropdown-icon supplied', () => { const icon = 'map' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { dropdownIcon: icon } }) - cy.get('.select-root') + getHostElement() .get(`div:contains(${ icon })`) .should('exist') }) @@ -62,21 +78,27 @@ describe('QSelect API', () => { describe('(prop): use-input', () => { it('should render an input inside the select', () => { - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { useInput: true } }) - cy.get('.select-root') + getHostElement() .get('input') .should('exist') }) - it('should not render an input by default', () => { - mount(WrapperOne) - cy.get('.select-root') + it('should render an input, but it shouldn\'t be visible', () => { + mountQSelect() + + getHostElement() .get('input') - .should('not.exist') + .should('not.be.visible') + }) + + it.skip('should not render an input by default', () => { + // Native input is now always rendered, due to having a target for autocomplete + // Refer to commit: https://github.com/quasarframework/quasar/commit/21a3af0dfe01bac0da617737562b599edee397a2 }) }) @@ -84,13 +106,13 @@ describe('QSelect API', () => { it('should use an input-debounce of 500ms by default', () => { const fn = cy.stub() const text = 'Hello there' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { useInput: true, onFilter: fn } }) - cy.get('.select-root') + getHostElement() .get('input') .type(text) .then(() => { @@ -105,14 +127,14 @@ describe('QSelect API', () => { it('should use a custom input-debounce', () => { const fn = cy.stub() const text = 'Hello there' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { useInput: true, onFilter: fn, inputDebounce: 800 } }) - cy.get('.select-root') + getHostElement() .get('input') .type(text) .wait(500) @@ -130,19 +152,19 @@ describe('QSelect API', () => { describe('Category: content|behavior', () => { describe('(prop): hide-dropdown-icon', () => { it('should show the dropdown-icon when this property is not supplied', () => { - mount(WrapperOne) - cy.get('.select-root') + mountQSelect() + getHostElement() .get('.q-icon') .should('exist') }) it('should hide the dropdown-icon when this property is true', () => { - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { hideDropdownIcon: true } }) - cy.get('.select-root') + getHostElement() .get('.q-icon') .should('not.exist') }) @@ -152,21 +174,21 @@ describe('QSelect API', () => { describe('Category: general', () => { describe('(prop): tabindex', () => { it('should have a default tabindex of 0', () => { - mount(WrapperOne) - cy.get('.select-root [tabindex="0"]') + mountQSelect() + getHostElement('[tabindex="0"]') .should('exist') }) it('should set the tabindex to the supplied value', () => { const tabindex = 2 - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { tabindex } }) - cy.get(`.select-root [tabindex="${ tabindex }"]`) + getHostElement(`[tabindex="${ tabindex }"]`) .should('exist') - cy.get('.select-root [tabindex="0"]') + getHostElement('[tabindex="0"]') .should('not.exist') }) }) @@ -176,13 +198,13 @@ describe('QSelect API', () => { describe('(prop): model-value', () => { it('should have the option selected passed in the model-value', () => { const modelValue = 'Option 1' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { modelValue, options: [ 'Option 1', 'Option 2', 'Option 3' ] } }) - cy.get('.select-root') + getHostElement() .should('include.text', modelValue) }) }) @@ -190,14 +212,14 @@ describe('QSelect API', () => { describe('(prop): emit-value', () => { it('should emit the value under the value key, if options are objects', () => { const fn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { emitValue: true, 'onUpdate:modelValue': fn, options: [ { label: 'Option 1', value: 1 }, { label: 'Option 2', value: 2 } ] } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains('Option 1') @@ -210,13 +232,13 @@ describe('QSelect API', () => { it('should emit the whole object by default if options are objects', () => { const fn = cy.stub() const options = [ { label: 'Option 1', value: 1 }, { label: 'Option 2', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { 'onUpdate:modelValue': fn, options } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains('Option 1') @@ -233,58 +255,60 @@ describe('QSelect API', () => { it('should select only one option by default', () => { const options = [ 'Option 1', 'Option 2' ] const model = ref(null) - mount(WrapperOne, { - attrs: { - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, + mountQSelect({ + props: { + ...vModelAdapter(model), options } }) - cy.get('.select-root') - .click() - cy.get('.q-menu') - .contains('Option 1') - .click() - .then(() => { - expect(model.value).to.equal(options[ 0 ]) - }) - cy.get('.q-menu') - .contains('Option 2') - .click() - .then(() => { - expect(model.value).to.equal(options[ 1 ]) - }) + + getHostElement().click() + cy.withinSelectMenu(() => { + cy.contains('Option 1') + .click() + .then(() => { + expect(model.value).to.equal(options[ 0 ]) + }) + }) + + getHostElement().click() + cy.withinSelectMenu(() => { + cy.contains('Option 2') + .click() + .then(() => { + expect(model.value).to.equal(options[ 1 ]) + }) + }) }) it('should select multiple options if multiple is true', () => { const options = [ 'Option 1', 'Option 2' ] const model = ref([]) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), multiple: true, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, options } }) - cy.get('.select-root') - .click() - cy.get('.q-menu') - .contains('Option 1') - .click() - .then(() => { - expect(model.value).to.eql([ options[ 0 ] ]) - }) - cy.get('.q-menu') - .contains('Option 2') - .click() - .then(() => { - expect(model.value).to.eql(options) - }) + + getHostElement().click() + cy.withinSelectMenu({ + persistent: true, + fn: () => { + cy.contains('Option 1') + .click() + .then(() => { + expect(model.value).to.eql([ options[ 0 ] ]) + }) + + cy.contains('Option 2') + .click() + .then(() => { + expect(model.value).to.eql(options) + }) + } + }) }) }) }) @@ -293,12 +317,12 @@ describe('QSelect API', () => { describe('(prop): options', () => { it('should show each option when opening the dropdown', () => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .children() @@ -313,17 +337,14 @@ describe('QSelect API', () => { it('should use the value key as option-value by default', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] const model = ref(null) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), options, - emitValue: true, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - } + emitValue: true } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains(options[ 0 ].label) @@ -336,18 +357,15 @@ describe('QSelect API', () => { it('should use a custom key supplied by option-value', () => { const options = [ { label: 'Option one', test: 1 }, { label: 'Option two', test: 2 } ] const model = ref(null) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), options, emitValue: true, - optionValue: 'test', - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - } + optionValue: 'test' } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains(options[ 0 ].label) @@ -360,18 +378,15 @@ describe('QSelect API', () => { it('should accept a function as option-value', () => { const options = [ { label: 'Option one', test: 1 }, { label: 'Option two', test: 2 } ] const model = ref(null) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), options, emitValue: true, - optionValue: (val) => val.test, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - } + optionValue: (val) => val.test } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains(options[ 0 ].label) @@ -385,12 +400,12 @@ describe('QSelect API', () => { describe('(prop): option-label', () => { it('should use the "label" key by default as option-label', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .children() @@ -400,13 +415,13 @@ describe('QSelect API', () => { it('should use the key supplied by option-label', () => { const options = [ { test: 'Option one', value: 1 }, { test: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionLabel: 'test' } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .children() @@ -416,13 +431,13 @@ describe('QSelect API', () => { it('should accept a function as option-label', () => { const options = [ { test: 'Option one', value: 1 }, { test: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionLabel: (item) => (item === null ? 'Null' : item.test) } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .children() @@ -433,12 +448,12 @@ describe('QSelect API', () => { describe('(prop): option-disable', () => { it('should use the "disable" key by default as option-disable', () => { const options = [ { label: 'Option one', value: 1, disable: true }, { label: 'Option two', value: 2, disable: true } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('[role="option"][aria-disabled="true"]') @@ -447,13 +462,13 @@ describe('QSelect API', () => { it('should use the key supplied by option-disable', () => { const options = [ { label: 'Option one', value: 1, test: true }, { label: 'Option two', value: 2, disable: true } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionDisable: 'test' } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('[role="option"][aria-disabled="true"]') @@ -463,13 +478,13 @@ describe('QSelect API', () => { it('should accept a function as option-disable', () => { const options = [ { label: 'Option one', value: 1, test: true }, { label: 'Option two', value: 2, disable: true } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionDisable: (item) => (item === null ? true : item.test) } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('[role="option"][aria-disabled="true"]') @@ -481,13 +496,13 @@ describe('QSelect API', () => { describe('(prop): options-dense', () => { it('should show options list dense', () => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionsDense: true } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('.q-item') @@ -498,13 +513,13 @@ describe('QSelect API', () => { describe('(prop): options-dark', () => { it('should show options list in dark mode', () => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionsDark: true } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('.q-item') @@ -515,14 +530,14 @@ describe('QSelect API', () => { describe('(prop): options-selected-class', () => { it('should have text-{color} applied as selected by default', () => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 'Option 1', color: 'orange' } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains('[role="option"]', options[ 0 ]) @@ -531,15 +546,15 @@ describe('QSelect API', () => { it('should not have default active class when passed option is empty', () => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 'Option 1', optionsSelectedClass: '', color: 'orange' } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains('[role="option"]', options[ 0 ]) @@ -548,15 +563,15 @@ describe('QSelect API', () => { it('should have class name supplied by options-selected-class on active item', () => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 'Option 1', optionsSelectedClass: 'test-class', color: 'orange' } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains('[role="option"]', options[ 0 ]) @@ -572,12 +587,12 @@ describe('QSelect API', () => { describe('(prop): options-html', () => { it('should not render options with html by default', () => { const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains('Option 1') @@ -587,13 +602,13 @@ describe('QSelect API', () => { it('should render options with html when options-html is true', () => { const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionsHtml: true } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .contains('Option 1') @@ -605,27 +620,27 @@ describe('QSelect API', () => { describe('(prop): options-cover', () => { it('should make the popup menu cover the select', (done) => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionsCover: true } }) - cy.get('.select-root') + getHostElement() .click() .isNotActionable(done) }) it('should not make the popup menu cover the select when use-input is used', () => { const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, optionsCover: true, useInput: true } }) - cy.get('.select-root') + getHostElement() .click() .click({ timeout: 100 }) }) @@ -634,14 +649,14 @@ describe('QSelect API', () => { describe('(prop): menu-shrink', () => { it('should shrink the menu', () => { const options = [ '1', '2', '3', '4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, menuShrink: true } }) let selectWidth = 0 - cy.get('.select-root') + getHostElement() .then(($el) => { selectWidth = $el[ 0 ].clientWidth }) @@ -656,26 +671,26 @@ describe('QSelect API', () => { describe('(prop): map-options', () => { it('should display the label of the selected value instead of the value itself', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1, mapOptions: true } }) - cy.get('.select-root') + getHostElement() .contains(options[ 0 ].label) }) it('should display the selected value as string by default', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1 } }) - cy.get('.select-root') + getHostElement() .contains(options[ 0 ].value) }) }) @@ -699,37 +714,37 @@ describe('QSelect API', () => { describe('(prop): display-value', () => { it('should override the default selection string', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1, displayValue: 'Test' } }) - cy.get('.select-root') + getHostElement() .should('not.contain', options[ 0 ].value) .should('contain', 'Test') }) it('should not override the default selection string when using `use-chips`', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1, displayValue: 'Test', useChips: true } }) - cy.get('.select-root') + getHostElement() .should('contain', options[ 0 ].value) .should('not.contain', 'Test') }) it('should not override the default selection string when using `selected` slot', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1, displayValue: 'Test' @@ -738,7 +753,7 @@ describe('QSelect API', () => { selected: () => 'Hello there' } }) - cy.get('.select-root') + getHostElement() .should('not.contain', options[ 0 ].value) .should('not.contain', 'Test') .should('contain', 'Hello there') @@ -748,14 +763,14 @@ describe('QSelect API', () => { describe('(prop): display-value-html', () => { it('should render the selected option as html', () => { const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: options[ 0 ], displayValueHtml: true } }) - cy.get('.select-root') + getHostElement() .contains('Option 1') .should('have.color', 'red') .should('have.css', 'font-weight', '700') @@ -764,8 +779,8 @@ describe('QSelect API', () => { it('should not render the selected option as html when using `selected` slot', () => { const html = 'Option 1' const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: options[ 0 ], displayValueHtml: true @@ -774,15 +789,15 @@ describe('QSelect API', () => { selected: () => html } }) - cy.get('.select-root') + getHostElement() .contains(html) }) it('should not render the selected option as html when using `selected-item` slot', () => { const html = 'Option 1' const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: options[ 0 ], displayValueHtml: true @@ -791,7 +806,7 @@ describe('QSelect API', () => { 'selected-item': () => html } }) - cy.get('.select-root') + getHostElement() .contains(html) }) }) @@ -799,22 +814,22 @@ describe('QSelect API', () => { describe('(prop): hide-selected', () => { it('should not show the selected value', () => { const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: options[ 0 ], hideSelected: true } }) - cy.get('.select-root') + getHostElement() .should('not.contain', options[ 0 ]) }) it('should set the value on the underlying input when using hide-selected', () => { // Todo: it its not really clear from the docs that you need to use `useInput` and `fillInput` together with this prop to achieve this const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: options[ 0 ], hideSelected: true, @@ -822,7 +837,7 @@ describe('QSelect API', () => { useInput: true } }) - cy.get('.select-root') + getHostElement() .get('input') .should('have.value', options[ 0 ]) }) @@ -832,25 +847,22 @@ describe('QSelect API', () => { it('should allow a maximum number of selections', () => { const max = 3 const options = [ '1', '2', '3', '4', '5' ] - const modelValue = ref([]) - mount(WrapperOne, { - attrs: { + const model = ref([]) + mountQSelect({ + props: { + ...vModelAdapter(model), options, - modelValue, maxValues: max, - 'onUpdate:modelValue': (val) => { - modelValue.value = val - }, multiple: true } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('[role="option"]') .click({ multiple: true }) .then(() => { - expect(modelValue.value.length).to.equal(max) + expect(model.value.length).to.equal(max) }) }) }) @@ -858,14 +870,14 @@ describe('QSelect API', () => { describe('(prop): use-chips', () => { it('should use QChips to show the selected value', () => { const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: options[ 0 ], useChips: true } }) - cy.get('.select-root') + getHostElement() .get('.q-chip') .should('contain', options[ 0 ]) }) @@ -876,13 +888,13 @@ describe('QSelect API', () => { describe('(prop): popup-content-class', () => { it('should apply the class to the popup element', () => { const className = 'test-class' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2 ' ], popupContentClass: className } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .should('have.class', className) @@ -892,13 +904,13 @@ describe('QSelect API', () => { describe('(prop): popup-content-style', () => { it('should apply the style definitions to the popup element', () => { const style = 'background: red;' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2 ' ], popupContentStyle: style } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .should('have.backgroundColor', 'red') @@ -908,13 +920,13 @@ describe('QSelect API', () => { describe('(prop): input-class', () => { it('should apply a class to the input element when using `useInput`', () => { const className = 'test-class' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { useInput: true, inputClass: className } }) - cy.get('.select-root') + getHostElement() .get('input') .should('have.class', className) }) @@ -923,13 +935,13 @@ describe('QSelect API', () => { describe('(prop): input-style', () => { it('should apply a style to the input element when using `useInput`', () => { const style = 'font-size: 30px' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { useInput: true, inputStyle: style } }) - cy.get('.select-root') + getHostElement() .get('input') .should('have.css', 'font-size', '30px') }) @@ -942,8 +954,8 @@ describe('QSelect API', () => { it('should display when something is selected', () => { const selectedString = 'Test slot selected' const options = [ 'Option 1', 'Option 2' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: options[ 0 ] }, @@ -951,7 +963,7 @@ describe('QSelect API', () => { selected: () => selectedString } }) - cy.get('.select-root') + getHostElement() .should('contain', selectedString) }) }) @@ -959,44 +971,44 @@ describe('QSelect API', () => { describe('(slot): loading', () => { it('should display when element is loading', () => { const loadingString = 'Test slot loading' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { loading: true }, slots: { loading: () => loadingString } }) - cy.get('.select-root') + getHostElement() .should('contain', loadingString) }) it('should not display when element is loading', () => { const loadingString = 'Test slot loading' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { loading: false }, slots: { loading: () => loadingString } }) - cy.get('.select-root') + getHostElement() .should('not.contain', loadingString) }) }) describe('(slot): before-options', () => { it('should display the slot content before the options', () => { - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2', '3' ] }, slots: { 'before-options': () => h('div', { class: 'dummyClass' }, 'Hello') } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .children().first() @@ -1006,15 +1018,15 @@ describe('QSelect API', () => { describe('(slot): after-options', () => { it('should display the slot content after the options', () => { - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2', '3' ] }, slots: { 'after-options': () => h('div', { class: 'dummyClass' }, 'Hello') } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .children().last() @@ -1025,15 +1037,15 @@ describe('QSelect API', () => { describe('(slot): no-option', () => { it('should display the slot content when there are no options', () => { const compareString = 'No options :(' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ ] }, slots: { 'no-option': () => compareString } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .should('contain', compareString) @@ -1041,8 +1053,8 @@ describe('QSelect API', () => { it('should pass the inputValue to the slot scope', () => { const compareString = 'No options :(' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ ], useInput: true }, @@ -1050,7 +1062,7 @@ describe('QSelect API', () => { 'no-option': (scope) => compareString + scope.inputValue } }) - cy.get('.select-root') + getHostElement() .click() .type('Hello') cy.get('.q-menu') @@ -1059,15 +1071,15 @@ describe('QSelect API', () => { it('should not display the slot content when there are options', () => { const compareString = 'No options :(' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2', '3' ] }, slots: { 'no-option': () => compareString } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .should('not.contain', compareString) @@ -1077,8 +1089,8 @@ describe('QSelect API', () => { describe('(slot): selected-item', () => { it('should override the default selection slot', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1 }, @@ -1086,15 +1098,15 @@ describe('QSelect API', () => { 'selected-item': () => 'Test' } }) - cy.get('.select-root') + getHostElement() .should('not.contain', options[ 0 ].value) .should('contain', 'Test') }) it('should pass the selected option index to the slot scope', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1 }, @@ -1102,14 +1114,14 @@ describe('QSelect API', () => { 'selected-item': (scope) => 'Test' + scope.index } }) - cy.get('.select-root') + getHostElement() .should('contain', 'Test0') }) it('should pass the selected option value to the slot scope', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, modelValue: 1 }, @@ -1117,29 +1129,26 @@ describe('QSelect API', () => { 'selected-item': (scope) => 'Test' + scope.opt } }) - cy.get('.select-root') + getHostElement() .should('contain', 'Test1') }) it('should pass a removeAtIndex function to the slot scope', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] const model = ref(1) - mount(WrapperOne, { - attrs: { - options, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - } + mountQSelect({ + props: { + ...vModelAdapter(model), + options }, slots: { 'selected-item': (scope) => h('button', { class: 'remove', onClick: () => scope.removeAtIndex(scope.index) }, 'Remove') } }) - cy.get('.select-root') + getHostElement() .get('button.remove') .click() - cy.get('.select-root') + getHostElement() .get('button.remove') .should('not.exist') }) @@ -1147,23 +1156,20 @@ describe('QSelect API', () => { it('should pass a toggleOption function to the slot scope', () => { const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ] const model = ref(1) - mount(WrapperOne, { - attrs: { - options, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - } + mountQSelect({ + props: { + ...vModelAdapter(model), + options }, slots: { 'selected-item': (scope) => h('button', { class: 'toggle', onClick: () => scope.toggleOption(2) }, 'Toggle' + scope.opt) } }) - cy.get('.select-root') + getHostElement() .get('button.toggle') .should('contain', 'Toggle1') .click() - cy.get('.select-root') + getHostElement() .get('button.toggle') .should('contain', 'Toggle2') }) @@ -1172,15 +1178,15 @@ describe('QSelect API', () => { describe('(slot): option', () => { it('should render a list of the provided slot as options', () => { const options = [ '1', '2', '3' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options }, slots: { option: (scope) => h('div', { class: 'custom-option' }, scope.opt) } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('.custom-option') @@ -1189,8 +1195,8 @@ describe('QSelect API', () => { it('should have a selected property in the scope', () => { const options = [ '1', '2', '3' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { modelValue: '1', options }, @@ -1198,7 +1204,7 @@ describe('QSelect API', () => { option: (scope) => h('div', { class: `custom-option-${ scope.selected }` }, scope.opt + scope.selected) } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('.custom-option-true') @@ -1212,8 +1218,8 @@ describe('QSelect API', () => { describe('(event): update:model-value', () => { it('should emit event when model value changes', () => { const fn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2', '3' ], modelValue: null, 'onUpdate:modelValue': fn @@ -1221,7 +1227,7 @@ describe('QSelect API', () => { }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('[role="option"]') @@ -1236,8 +1242,8 @@ describe('QSelect API', () => { describe('(event): input-value', () => { it('should emit event when text input changes', () => { const fn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { modelValue: null, onInputValue: fn, useInput: true @@ -1245,7 +1251,7 @@ describe('QSelect API', () => { }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .get('input') .type('h') .then(() => { @@ -1258,20 +1264,17 @@ describe('QSelect API', () => { it('should emit event when a selected item is removed from selection', () => { const fn = cy.stub() const model = ref([ '2', '3' ]) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), onRemove: fn, multiple: true, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, options: [ '1', '2', '3' ] } }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('[role="option"]') @@ -1295,20 +1298,17 @@ describe('QSelect API', () => { it('should emit event when an option is added to the selection', () => { const fn = cy.stub() const model = ref([ '2' ]) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), onAdd: fn, multiple: true, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, options: [ '1', '2', '3' ] } }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .get('[role="option"]') @@ -1325,21 +1325,18 @@ describe('QSelect API', () => { it('should emit event when something is typed into the input field and enter is pressed', () => { const fn = cy.stub() const model = ref([ '2' ]) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), onNewValue: fn, multiple: true, useInput: true, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, hideDropdownIcon: true } }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .get('input') .type('100') .then(() => { @@ -1353,22 +1350,19 @@ describe('QSelect API', () => { it('should add the value to the model when the doneFn is called', () => { const model = ref([ '2' ]) - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { + ...vModelAdapter(model), onNewValue: (val, doneFn) => { doneFn(val) }, multiple: true, useInput: true, - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, hideDropdownIcon: true } }) - cy.get('.select-root') + getHostElement() .get('input') .type('100') .type('{enter}') @@ -1381,8 +1375,8 @@ describe('QSelect API', () => { describe('(event): filter', () => { it('should emit event when something is typed into the input field', () => { const fn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { onFilter: fn, useInput: true, inputDebounce: 0 @@ -1390,7 +1384,7 @@ describe('QSelect API', () => { }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .get('input') .type('h') .then(() => { @@ -1403,8 +1397,8 @@ describe('QSelect API', () => { it('should emit event when the the filterFn has not called the doneFn yet and a new filter is requested', () => { const fn = cy.stub() const filterFn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { onFilter: filterFn, onFilterAbort: fn, useInput: true, @@ -1413,7 +1407,7 @@ describe('QSelect API', () => { }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .get('input') .click() .then(() => { @@ -1428,8 +1422,8 @@ describe('QSelect API', () => { it('should not emit event when the filter has called its doneFn', () => { const fn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { onFilter: (val, doneFn) => { doneFn() }, @@ -1440,7 +1434,7 @@ describe('QSelect API', () => { }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .get('input') .click() .then(() => { @@ -1456,15 +1450,15 @@ describe('QSelect API', () => { describe('(event): popup-show', () => { it('should emit event when the options are shown', () => { const fn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { onPopupShow: fn, options: [ '1', '2', '3' ] } }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .click() .then(() => { expect(fn).to.be.called @@ -1475,15 +1469,15 @@ describe('QSelect API', () => { describe('(event): popup-hide', () => { it('should emit event when the options are hidden', () => { const fn = cy.stub() - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { onPopupHide: fn, options: [ '1', '2', '3' ] } }) expect(fn).not.to.be.called - cy.get('.select-root') + getHostElement() .click() .then(() => { expect(fn).not.to.be.called @@ -1508,16 +1502,16 @@ describe('QSelect API', () => { describe('Methods', () => { describe('(method): focus', () => { it('should focus the component', () => { - mount(WrapperOne) + mountQSelect() - cy.dataCy('select') + getHostElement() .get('[tabindex="0"]') .should('not.have.focus') - cy.dataCy('select') + getHostElement() .then(() => { - Cypress.vueWrapper.vm.compRef.focus() + Cypress.vueWrapper.vm.focus() }) - cy.dataCy('select') + getHostElement() .get('[tabindex="0"]') .should('have.focus') }) @@ -1525,8 +1519,8 @@ describe('QSelect API', () => { describe('(method): showPopup', () => { it('should open the popup and focus the component', () => { - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2' ] } }) @@ -1534,11 +1528,11 @@ describe('QSelect API', () => { cy.get('.q-menu') .should('not.exist') .then(() => { - Cypress.vueWrapper.vm.compRef.showPopup() + Cypress.vueWrapper.vm.showPopup() }) cy.get('.q-menu') .should('be.visible') - cy.dataCy('select') + getHostElement() .get('[tabindex="0"]') .should('have.focus') }) @@ -1546,18 +1540,18 @@ describe('QSelect API', () => { describe('(method): hidePopup', () => { it('should hide the popup', () => { - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options: [ '1', '2' ] } }) - cy.get('.select-root') + getHostElement() .click() cy.get('.q-menu') .should('be.visible') .then(() => { - Cypress.vueWrapper.vm.compRef.hidePopup() + Cypress.vueWrapper.vm.hidePopup() }) cy.get('.q-menu') .should('not.exist') @@ -1568,19 +1562,16 @@ describe('QSelect API', () => { it('should remove a selected option at the correct index', () => { const options = [ '1', '2', '3', '4' ] const model = ref([ '1', '2', '4' ]) - mount(WrapperOne, { - attrs: { - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, + mountQSelect({ + props: { + ...vModelAdapter(model), multiple: true, options } }) .then(() => { expect(model.value.includes('4')).to.be.true - Cypress.vueWrapper.vm.compRef.removeAtIndex(2) + Cypress.vueWrapper.vm.removeAtIndex(2) expect(model.value.includes('4')).to.be.false }) }) @@ -1589,38 +1580,32 @@ describe('QSelect API', () => { describe('(method): add', () => { it('should add a selected option', () => { const model = ref([ '1', '2' ]) - mount(WrapperOne, { - attrs: { - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, + mountQSelect({ + props: { + ...vModelAdapter(model), multiple: true } }) .then(() => { expect(model.value.includes('100')).to.be.false - Cypress.vueWrapper.vm.compRef.add('100') + Cypress.vueWrapper.vm.add('100') expect(model.value.includes('100')).to.be.true }) }) it('should not add a duplicate option when unique is true', () => { const model = ref([ '1', '2' ]) - mount(WrapperOne, { - attrs: { - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, + mountQSelect({ + props: { + ...vModelAdapter(model), multiple: true } }) .then(() => { expect(model.value.length).to.be.equal(2) - Cypress.vueWrapper.vm.compRef.add('2', true) + Cypress.vueWrapper.vm.add('2', true) expect(model.value.length).to.be.equal(2) - Cypress.vueWrapper.vm.compRef.add('2') + Cypress.vueWrapper.vm.add('2') expect(model.value.length).to.be.equal(3) }) }) @@ -1629,24 +1614,21 @@ describe('QSelect API', () => { describe('(method): toggleOption', () => { it('should toggle an option', () => { const model = ref([ '1', '2' ]) - mount(WrapperOne, { - attrs: { - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, + mountQSelect({ + props: { + ...vModelAdapter(model), multiple: true } }) .then(() => { expect(model.value.length).to.be.equal(2) - Cypress.vueWrapper.vm.compRef.toggleOption('2') + Cypress.vueWrapper.vm.toggleOption('2') expect(model.value.length).to.be.equal(1) }) // When not using this wait this test will succeed on `open-ct` but fail on `run-ct` .wait(50) .then(() => { - Cypress.vueWrapper.vm.compRef.toggleOption('2') + Cypress.vueWrapper.vm.toggleOption('2') expect(model.value.length).to.be.equal(2) }) }) @@ -1655,25 +1637,22 @@ describe('QSelect API', () => { // should this be consistent? E.g. use `true` as argument when multiple is true by default but make sure it can be overridden. it('should close the menu and clear the filter', () => { const model = ref('1') - mount(WrapperOne, { - attrs: { - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, + mountQSelect({ + props: { + ...vModelAdapter(model), options: [ '1', '2' ], useInput: true } }) - cy.get('.select-root') + getHostElement() .click() .get('input') .type('h') cy.get('.q-menu') .should('be.visible') .then(() => { - Cypress.vueWrapper.vm.compRef.toggleOption('2') + Cypress.vueWrapper.vm.toggleOption('2') }) cy.get('.q-menu') .should('not.exist') @@ -1683,25 +1662,22 @@ describe('QSelect API', () => { it('should not close the menu and clear the filter when keepOpen is true', () => { const model = ref('1') - mount(WrapperOne, { - attrs: { - modelValue: model, - 'onUpdate:modelValue': (val) => { - model.value = val - }, + mountQSelect({ + props: { + ...vModelAdapter(model), options: [ '1', '2' ], useInput: true } }) - cy.get('.select-root') + getHostElement() .click() .get('input') .type('h') cy.get('.q-menu') .should('be.visible') .then(() => { - Cypress.vueWrapper.vm.compRef.toggleOption('2', true) + Cypress.vueWrapper.vm.toggleOption('2', true) }) cy.get('.q-menu') .should('be.visible') @@ -1713,15 +1689,15 @@ describe('QSelect API', () => { describe('(method): setOptionIndex', () => { it('should set an option from the menu dropdown as focused', () => { const options = [ '1', '2', '3', '4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options } }) - cy.get('.select-root') + getHostElement() .click() .then(() => { - Cypress.vueWrapper.vm.compRef.setOptionIndex(0) + Cypress.vueWrapper.vm.setOptionIndex(0) }) .get('[role="option"]') .first() @@ -1732,21 +1708,21 @@ describe('QSelect API', () => { describe('(method): moveOptionSelection', () => { it('should move the optionSelection by some index offset', () => { const options = [ '1', '2', '3', '4' ] - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options } }) - cy.get('.select-root') + getHostElement() .click() .then(() => { - Cypress.vueWrapper.vm.compRef.setOptionIndex(0) + Cypress.vueWrapper.vm.setOptionIndex(0) }) .get('[role="option"]') .first() .should('have.class', 'q-manual-focusable--focused') .then(() => { - Cypress.vueWrapper.vm.compRef.moveOptionSelection(3) + Cypress.vueWrapper.vm.moveOptionSelection(3) }) .get('[role="option"]') .last() @@ -1759,18 +1735,18 @@ describe('QSelect API', () => { const options = [ '1', '2', '3', '4' ] const fn = cy.stub() const text = 'test' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, useInput: true, onFilter: fn } }) - cy.get('.select-root') + getHostElement() .click() .then(() => { expect(fn).not.to.be.calledWith(text) - Cypress.vueWrapper.vm.compRef.filter(text) + Cypress.vueWrapper.vm.filter(text) expect(fn).to.be.calledWith(text) }) }) @@ -1787,18 +1763,18 @@ describe('QSelect API', () => { const options = [ '1', '2', '3', '4' ] const fn = cy.stub() const text = 'test' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, useInput: true, onFilter: fn } }) - cy.get('.select-root') + getHostElement() .click() .then(() => { expect(fn).not.to.be.calledWith(text) - Cypress.vueWrapper.vm.compRef.updateInputValue(text) + Cypress.vueWrapper.vm.updateInputValue(text) expect(fn).to.be.calledWith(text) }) .get('input') @@ -1809,18 +1785,18 @@ describe('QSelect API', () => { const options = [ '1', '2', '3', '4' ] const fn = cy.stub() const text = 'test' - mount(WrapperOne, { - attrs: { + mountQSelect({ + props: { options, useInput: true, onFilter: fn } }) - cy.get('.select-root') + getHostElement() .click() .then(() => { expect(fn).not.to.be.calledWith(text) - Cypress.vueWrapper.vm.compRef.updateInputValue(text, true) + Cypress.vueWrapper.vm.updateInputValue(text, true) expect(fn).not.to.be.calledWith(text) }) .get('input') @@ -1831,17 +1807,17 @@ describe('QSelect API', () => { describe('(method): isOptionSelected', () => { it('should tell when an option is selected', () => { const options = [ '1', '2', '3', '4' ] - const model = ref([ '1', '2', '4' ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = [ '1', '2', '4' ] + mountQSelect({ + props: { + modelValue, multiple: true, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.isOptionSelected(options[ 0 ])).to.be.true - expect(Cypress.vueWrapper.vm.compRef.isOptionSelected(options[ 2 ])).to.be.false + expect(Cypress.vueWrapper.vm.isOptionSelected(options[ 0 ])).to.be.true + expect(Cypress.vueWrapper.vm.isOptionSelected(options[ 2 ])).to.be.false }) }) }) @@ -1849,44 +1825,44 @@ describe('QSelect API', () => { describe('(method): getEmittingOptionValue', () => { it('should return the emit value with plain options', () => { const options = [ '1', '2', '3', '4' ] - const model = ref('1') - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = '1' + mountQSelect({ + props: { + modelValue, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ]) + expect(Cypress.vueWrapper.vm.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ]) }) }) it('should return the emit value with object options', () => { const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ] - const model = ref(options[ 0 ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = options[ 0 ] + mountQSelect({ + props: { + modelValue, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ]) + expect(Cypress.vueWrapper.vm.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ]) }) }) it('should respect emit-value when using options', () => { const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ] - const model = ref(options[ 0 ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = options[ 0 ] + mountQSelect({ + props: { + modelValue, options, emitValue: true } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ].value) + expect(Cypress.vueWrapper.vm.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ].value) }) }) }) @@ -1894,44 +1870,44 @@ describe('QSelect API', () => { describe('(method): getOptionValue', () => { it('should return the option value with plain options', () => { const options = [ '1', '2', '3', '4' ] - const model = ref('1') - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = '1' + mountQSelect({ + props: { + modelValue, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getOptionValue(options[ 2 ])).to.equal(options[ 2 ]) + expect(Cypress.vueWrapper.vm.getOptionValue(options[ 2 ])).to.equal(options[ 2 ]) }) }) it('should return the option value with object options (value by default)', () => { const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ] - const model = ref(options[ 0 ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = options[ 0 ] + mountQSelect({ + props: { + modelValue, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getOptionValue(options[ 2 ])).to.equal(options[ 2 ].value) + expect(Cypress.vueWrapper.vm.getOptionValue(options[ 2 ])).to.equal(options[ 2 ].value) }) }) it('should respect the option-value option', () => { const options = [ { label: '1', test: 1 }, { label: '2', test: 2 }, { label: '3', test: 3 } ] - const model = ref(options[ 0 ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = options[ 0 ] + mountQSelect({ + props: { + modelValue, options, optionValue: 'test' } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getOptionValue(options[ 2 ])).to.equal(options[ 2 ].test) + expect(Cypress.vueWrapper.vm.getOptionValue(options[ 2 ])).to.equal(options[ 2 ].test) }) }) }) @@ -1939,44 +1915,44 @@ describe('QSelect API', () => { describe('(method): getOptionLabel', () => { it('should return the option label with plain options', () => { const options = [ '1', '2', '3', '4' ] - const model = ref('1') - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = '1' + mountQSelect({ + props: { + modelValue, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ]) + expect(Cypress.vueWrapper.vm.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ]) }) }) it('should return the option label with object options (label by default)', () => { const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ] - const model = ref(options[ 0 ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = options[ 0 ] + mountQSelect({ + props: { + modelValue, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ].label) + expect(Cypress.vueWrapper.vm.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ].label) }) }) it('should respect the option-value option', () => { const options = [ { test: '1', value: 1 }, { test: '2', value: 2 }, { test: '3', value: 3 } ] - const model = ref(options[ 0 ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = options[ 0 ] + mountQSelect({ + props: { + modelValue, options, optionLabel: 'test' } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ].test) + expect(Cypress.vueWrapper.vm.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ].test) }) }) }) @@ -1984,18 +1960,18 @@ describe('QSelect API', () => { describe('(method): isOptionDisabled', () => { it('should return if an option is disabled correctly', () => { const options = [ { label: '1', value: 1, disable: true }, { label: '2', value: 2 }, { label: '3', value: 3 } ] - const model = ref(options[ 0 ]) - mount(WrapperOne, { - attrs: { - modelValue: model, + const modelValue = options[ 0 ] + mountQSelect({ + props: { + modelValue, options } }) .then(() => { - expect(Cypress.vueWrapper.vm.compRef.isOptionDisabled(options[ 0 ])).to.be.true + expect(Cypress.vueWrapper.vm.isOptionDisabled(options[ 0 ])).to.be.true // This currently fails: https://github.com/quasarframework/quasar/issues/12046 - // expect(Cypress.vueWrapper.vm.compRef.isOptionDisabled(options[ 1 ])).to.be.false - // expect(Cypress.vueWrapper.vm.compRef.isOptionDisabled(options[ 2 ])).to.be.false + // expect(Cypress.vueWrapper.vm.isOptionDisabled(options[ 1 ])).to.be.false + // expect(Cypress.vueWrapper.vm.isOptionDisabled(options[ 2 ])).to.be.false }) }) }) diff --git a/ui/src/components/select/__tests__/WrapperOne.vue b/ui/src/components/select/__tests__/WrapperOne.vue deleted file mode 100644 index 4d89eb63a27..00000000000 --- a/ui/src/components/select/__tests__/WrapperOne.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - diff --git a/ui/src/composables/private/__tests__/use-anchor.spec.js b/ui/src/composables/private/__tests__/use-anchor.spec.js index 496c24fac12..a6b9b70ef35 100644 --- a/ui/src/composables/private/__tests__/use-anchor.spec.js +++ b/ui/src/composables/private/__tests__/use-anchor.spec.js @@ -7,7 +7,7 @@ describe('use-anchor API', () => { describe('(prop): target', () => { it('should use another target using a CSS selector', () => { mount(WrapperOne, { - attrs: { + props: { target: '.other-target' } }) @@ -27,7 +27,7 @@ describe('use-anchor API', () => { it('should not show when target is false', () => { mount(WrapperOne, { - attrs: { + props: { target: false } }) @@ -42,7 +42,7 @@ describe('use-anchor API', () => { describe('(prop): no-parent-event', () => { it('should not show when clicking parent with no-parent-event true', () => { mount(WrapperOne, { - attrs: { + props: { 'no-parent-event': true } }) @@ -55,7 +55,7 @@ describe('use-anchor API', () => { it('should show when clicking parent with no-parent-event false', () => { mount(WrapperOne, { - attrs: { + props: { 'no-parent-event': false } }) @@ -70,7 +70,7 @@ describe('use-anchor API', () => { describe('(prop): context-menu', () => { it('should not show when left clicking parent', () => { mount(WrapperOne, { - attrs: { + props: { 'context-menu': true } }) @@ -83,7 +83,7 @@ describe('use-anchor API', () => { it('should show when right clicking parent', () => { mount(WrapperOne, { - attrs: { + props: { 'context-menu': true } }) diff --git a/ui/src/composables/private/__tests__/use-field.spec.js b/ui/src/composables/private/__tests__/use-field.spec.js index 4515361279c..cba362d9d9c 100644 --- a/ui/src/composables/private/__tests__/use-field.spec.js +++ b/ui/src/composables/private/__tests__/use-field.spec.js @@ -42,7 +42,7 @@ describe('use-field API', () => { it('should show the label when supplied', () => { const label = 'Select something' mount(FieldWrapper, { - attrs: { + props: { label } }) @@ -53,7 +53,7 @@ describe('use-field API', () => { it('should show the label centered when not focused', () => { const label = 'Select something' mount(FieldWrapper, { - attrs: { + props: { label } }) @@ -66,7 +66,7 @@ describe('use-field API', () => { it('should show the label stacked when focused', () => { const label = 'Select something' mount(FieldWrapper, { - attrs: { + props: { label } }) @@ -82,7 +82,7 @@ describe('use-field API', () => { it('should show the label stacked', () => { const label = 'Select something' mount(FieldWrapper, { - attrs: { + props: { label, stackLabel: true } @@ -97,7 +97,7 @@ describe('use-field API', () => { it('should show a hint text', () => { const hint = 'Select something' mount(FieldWrapper, { - attrs: { + props: { hint } }) @@ -110,7 +110,7 @@ describe('use-field API', () => { it('should not show a hint text when not focused', () => { const hint = 'Select something' mount(FieldWrapper, { - attrs: { + props: { hint, hideHint: true } @@ -121,7 +121,7 @@ describe('use-field API', () => { it('should show a hint text when focused', () => { const hint = 'Select something' mount(FieldWrapper, { - attrs: { + props: { hint, hideHint: true } diff --git a/ui/src/composables/private/__tests__/use-model-toggle.spec.js b/ui/src/composables/private/__tests__/use-model-toggle.spec.js index 356f899921c..1f65fe43ae9 100644 --- a/ui/src/composables/private/__tests__/use-model-toggle.spec.js +++ b/ui/src/composables/private/__tests__/use-model-toggle.spec.js @@ -3,40 +3,41 @@ import { mount } from '@cypress/vue' import WrapperOne from './../../../components/menu/__tests__/WrapperOne.vue' import WrapperTwo from './../../../components/menu/__tests__/WrapperTwo.vue' import { ref } from 'vue' +import { vModelAdapter } from '../../../../test/cypress/helpers/v-model-adapter' describe('use-model-toggle API', () => { describe('Props', () => { describe('Category: model', () => { describe('(prop): model-value', () => { it('should open the dialog when modifying the model-value', () => { - const modelValue = ref(false) + const model = ref(false) mount(WrapperOne, { - attrs: { - modelValue + props: { + ...vModelAdapter(model) } }) cy.dataCy('wrapper') cy.dataCy('menu') .should('not.exist') .then(() => { - modelValue.value = true + model.value = true cy.dataCy('menu') .should('exist') }) }) it('should close the dialog when modifying the model-value', () => { - const modelValue = ref(true) + const model = ref(true) mount(WrapperOne, { - attrs: { - modelValue + props: { + ...vModelAdapter(model) } }) cy.dataCy('wrapper') cy.dataCy('menu') .should('exist') .then(() => { - modelValue.value = false + model.value = false cy.dataCy('menu') .should('not.exist') }) @@ -50,7 +51,7 @@ describe('use-model-toggle API', () => { it('should emit @update:model-value event when state changes', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { 'onUpdate:modelValue': fn } }) @@ -70,7 +71,7 @@ describe('use-model-toggle API', () => { it('should emit @show event when menu is triggered by parent', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onShow: fn } }) @@ -89,7 +90,7 @@ describe('use-model-toggle API', () => { it('should emit @show event when component is triggered with the show() method', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onShow: fn } }) @@ -111,7 +112,7 @@ describe('use-model-toggle API', () => { it('should emit @before-show event when menu is triggered by parent', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onBeforeShow: fn } }) @@ -129,7 +130,7 @@ describe('use-model-toggle API', () => { it('should emit @before-show event when component is triggered with the show() method', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onBeforeShow: fn } }) @@ -150,7 +151,7 @@ describe('use-model-toggle API', () => { it('should emit @hide event when menu is triggered by parent', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onHide: fn } }) @@ -175,7 +176,7 @@ describe('use-model-toggle API', () => { it('should emit @hide event when component is triggered with the show() method', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onHide: fn } }) @@ -203,7 +204,7 @@ describe('use-model-toggle API', () => { it('should emit @before-hide event when menu is triggered by parent', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onBeforeHide: fn } }) @@ -228,7 +229,7 @@ describe('use-model-toggle API', () => { it('should emit @before-hide event when component is triggered with the show() method', () => { const fn = cy.stub() mount(WrapperOne, { - attrs: { + props: { onBeforeHide: fn } }) diff --git a/ui/src/composables/private/__tests__/use-transition.spec.js b/ui/src/composables/private/__tests__/use-transition.spec.js index 182eebff2e2..ffa6a908b34 100644 --- a/ui/src/composables/private/__tests__/use-transition.spec.js +++ b/ui/src/composables/private/__tests__/use-transition.spec.js @@ -16,7 +16,7 @@ describe('use-transition API', () => { it('should use a different show transition if defined', () => { const transition = 'scale' mount(WrapperOne, { - attrs: { + props: { transitionShow: transition } }) @@ -43,7 +43,7 @@ describe('use-transition API', () => { it('should use a different hide transition if defined', () => { const transition = 'scale' mount(WrapperOne, { - attrs: { + props: { transitionHide: transition } }) @@ -64,7 +64,7 @@ describe('use-transition API', () => { cy.dataCy('wrapper') .click() .wait(300) - cy.dataCy('menu', { timeout: 0 }) // Disable retry + cy.dataCy('menu', { timeout: 350 }) .should('not.have.class', 'q-transition--fade-enter-active') }) @@ -79,7 +79,7 @@ describe('use-transition API', () => { it('should be done after a custom 1000ms passed', () => { mount(WrapperOne, { - attrs: { + props: { transitionDuration: 1000 } }) @@ -92,7 +92,7 @@ describe('use-transition API', () => { it('should not be done before a custom 1000ms passed', () => { mount(WrapperOne, { - attrs: { + props: { transitionDuration: 1000 } }) diff --git a/ui/src/composables/private/__tests__/use-validate.spec.js b/ui/src/composables/private/__tests__/use-validate.spec.js index ab1e9a75138..85c68895952 100644 --- a/ui/src/composables/private/__tests__/use-validate.spec.js +++ b/ui/src/composables/private/__tests__/use-validate.spec.js @@ -37,7 +37,7 @@ describe('use-validate API', () => { it('should show an error-message when error is true', () => { const message = 'Please select something' mount(FieldWrapper, { - attrs: { + props: { error: true, errorMessage: message } @@ -49,7 +49,7 @@ describe('use-validate API', () => { it('should not show an error-message when error is false', () => { const message = 'Please select something' mount(FieldWrapper, { - attrs: { + props: { error: false, errorMessage: message } @@ -62,7 +62,7 @@ describe('use-validate API', () => { describe('(prop): no-error-icon', () => { it('should not show an error icon when error is true', () => { mount(FieldWrapper, { - attrs: { + props: { error: true, noErrorIcon: true } @@ -74,7 +74,7 @@ describe('use-validate API', () => { it('should show an error icon when error is true an no-error-icon is false', () => { mount(FieldWrapper, { - attrs: { + props: { error: true, noErrorIcon: false } diff --git a/ui/test/cypress/helpers/v-model-adapter.js b/ui/test/cypress/helpers/v-model-adapter.js new file mode 100644 index 00000000000..fb5093786da --- /dev/null +++ b/ui/test/cypress/helpers/v-model-adapter.js @@ -0,0 +1,17 @@ +import { watch } from 'vue' + +// VTU won't accept reactive v-model binding, this adapter manually +// set the model prop each time a new value is emitted +// See https://github.com/vuejs/test-utils/discussions/279 + +// See https://github.com/vuejs/test-utils/issues/871 +export function vModelAdapter (modelRef, modelName = 'modelValue') { + watch(modelRef, (value) => Cypress.vueWrapper.setProps({ [ modelName ]: value })) + + return { + [ modelName ]: modelRef.value, + [ `onUpdate:${ modelName }` ]: (emittedValue) => { + modelRef.value = emittedValue + } + } +} diff --git a/ui/tsconfig.json b/ui/tsconfig.json index b8a21b10d21..41f26abc717 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "jsx": "preserve", "allowJs": true, "target": "esnext", "module": "esnext", @@ -8,4 +9,4 @@ "outDir": "./dist" }, "include": ["./src/**/*", "./test/cypress/**/*.d.ts", "./test/cypress/support/commands.js"] -} \ No newline at end of file +}