mirror of
https://github.com/Elvanos/fantasia-archive.git
synced 2024-05-18 11:13:15 +12:00
Cleaned up frontend testing & Readme file
This commit is contained in:
parent
5e7526882c
commit
8a0ef51133
28
README.md
28
README.md
|
@ -11,42 +11,44 @@ Make sure you are running this with Node v16.17.0 ("nvm" is great for these olde
|
||||||
|
|
||||||
##### Ensure that the Yarn global install location is in your PATH after install. (details in article linked above)
|
##### Ensure that the Yarn global install location is in your PATH after install. (details in article linked above)
|
||||||
|
|
||||||
```bash
|
```
|
||||||
yarn global add @quasar/cli
|
yarn global add @quasar/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install the dependencies and set up the project
|
## Install the dependencies and set up the project
|
||||||
```bash
|
```
|
||||||
yarn
|
yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
### Start the app in Quasar development mode (hot-code reloading, error reporting, etc.)
|
### Start the app in Quasar development mode (hot-code reloading, error reporting, etc.)
|
||||||
```bash
|
```
|
||||||
quasar dev -m electron
|
quasar dev -m electron
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build the app for production
|
### Build the app for production
|
||||||
```bash
|
```
|
||||||
quasar build
|
quasar build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing:
|
### Testing:
|
||||||
|
|
||||||
|
> Keep in mind that Cypress tests are limited to front-end testing due to the nature of Electron's nodeJS-based backend. Anything in Electron's main and preload will NOT work.
|
||||||
|
|
||||||
#### Unit test - with pretty web-UI
|
#### Unit test - with pretty web-UI
|
||||||
```bash
|
```
|
||||||
test:unit:ui
|
test:unit:ui
|
||||||
```
|
```
|
||||||
#### Unit test - without any UI, fully in a terminal
|
#### Unit test - without any UI, fully in a terminal
|
||||||
```bash
|
|
||||||
test:unit
|
|
||||||
```
|
```
|
||||||
#### Component test - via Cypress, pick Electron on the config screen (I suggest turning on the electron dev window first, the test is a bit buggy sometimes)
|
test:unit:ci
|
||||||
```bash
|
|
||||||
test:component
|
|
||||||
```
|
```
|
||||||
#### e2e test - via Cypress, pick Electron on the config screen (I suggest turning on the electron dev window first, the test is a bit buggy sometimes)
|
#### Component test, Frontend - via Cypress, pick Electron on the config screen (I suggest turning on the electron dev window first, the test is a bit buggy sometimes)
|
||||||
```bash
|
```
|
||||||
test:e2e
|
test:component:frontend
|
||||||
|
```
|
||||||
|
#### E2E test, Froentend - via Cypress, pick Electron on the config screen (I suggest turning on the electron dev window first, the test is a bit buggy sometimes)
|
||||||
|
```
|
||||||
|
test:e2e:frontend
|
||||||
```
|
```
|
||||||
|
|
||||||
### Customize the configuration
|
### Customize the configuration
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default defineConfig({
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
supportFile: 'test/cypress/support/component.ts',
|
supportFile: 'test/cypress/support/component.ts',
|
||||||
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
specPattern: 'src/**/*.cy.test.{js,jsx,ts,tsx}',
|
||||||
indexHtmlFile: 'test/cypress/support/component-index.html',
|
indexHtmlFile: 'test/cypress/support/component-index.html',
|
||||||
devServer: injectQuasarDevServerConfig()
|
devServer: injectQuasarDevServerConfig()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
"test:unit:ui": "vitest --ui",
|
"test:unit:ui": "vitest --ui",
|
||||||
"test:unit": "vitest",
|
"test:unit": "vitest",
|
||||||
"test:unit:ci": "vitest run",
|
"test:unit:ci": "vitest run",
|
||||||
"test:e2e": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress open --e2e\"",
|
"test:e2e:frontend": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress open --e2e\"",
|
||||||
"test:e2e:ci": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress run --e2e\"",
|
"test:e2e:frontend:ci": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress run --e2e\"",
|
||||||
"test:component": "cross-env NODE_ENV=test cypress open --component",
|
"test:component:frontend": "cross-env NODE_ENV=test cypress open --component",
|
||||||
"test:component:ci": "cross-env NODE_ENV=test cypress run --component"
|
"test:component:frontend:ci": "cross-env NODE_ENV=test cypress run --component"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.0.10",
|
"@electron/remote": "^2.0.10",
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import GlobalWindowButtons from './GlobalWindowButtons.vue'
|
||||||
|
|
||||||
|
describe('Component test - GlobalWindowButtons', () => {
|
||||||
|
it('should have a `accent` color & `dark` background-color', () => {
|
||||||
|
cy.mount(GlobalWindowButtons)
|
||||||
|
|
||||||
|
cy.get('.q-btn-group')
|
||||||
|
.should('have.backgroundColor', 'var(--q-dark)')
|
||||||
|
.should('have.color', 'var(--q-accent)')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { installQuasar } from '@quasar/quasar-app-extension-testing-unit-vitest'
|
||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import GlobalWindowButtons from './GlobalWindowButtons.vue'
|
||||||
|
|
||||||
|
installQuasar()
|
||||||
|
|
||||||
|
describe('Unit test - GlobalWindowButtons component', () => {
|
||||||
|
it('should mount three buttons', () => {
|
||||||
|
const wrapper = mount(GlobalWindowButtons)
|
||||||
|
expect(wrapper.findAll('.q-btn')).toHaveLength(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have `minimize` button', () => {
|
||||||
|
const wrapper = mount(GlobalWindowButtons)
|
||||||
|
expect(wrapper.findAll('.globalWindowButtons__minimize')).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have `resize` button', () => {
|
||||||
|
const wrapper = mount(GlobalWindowButtons)
|
||||||
|
expect(wrapper.findAll('.globalWindowButtons__minimize')).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have `close` button', () => {
|
||||||
|
const wrapper = mount(GlobalWindowButtons)
|
||||||
|
expect(wrapper.findAll('.globalWindowButtons__minimize')).toHaveLength(1)
|
||||||
|
})
|
||||||
|
})
|
|
@ -10,6 +10,7 @@
|
||||||
:ripple="false"
|
:ripple="false"
|
||||||
dark
|
dark
|
||||||
size="xs"
|
size="xs"
|
||||||
|
class="globalWindowButtons__minimize"
|
||||||
@click="minimizeWindow()"
|
@click="minimizeWindow()"
|
||||||
>
|
>
|
||||||
<q-tooltip
|
<q-tooltip
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
:ripple="false"
|
:ripple="false"
|
||||||
dark
|
dark
|
||||||
size="xs"
|
size="xs"
|
||||||
|
class="globalWindowButtons__resize"
|
||||||
@click="[resizeWindow(),checkIfWindowMaximized()]"
|
@click="[resizeWindow(),checkIfWindowMaximized()]"
|
||||||
>
|
>
|
||||||
<q-tooltip
|
<q-tooltip
|
||||||
|
@ -44,6 +46,7 @@
|
||||||
:ripple="false"
|
:ripple="false"
|
||||||
dark
|
dark
|
||||||
size="xs"
|
size="xs"
|
||||||
|
class="globalWindowButtons__close"
|
||||||
@click="tryCloseWindow()"
|
@click="tryCloseWindow()"
|
||||||
>
|
>
|
||||||
<q-tooltip
|
<q-tooltip
|
||||||
|
@ -61,20 +64,23 @@
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
// TODO Add all tests!
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Triggers minimize of the window by the minimize button click
|
Triggers minimize of the window by the minimize button click
|
||||||
*/
|
*/
|
||||||
const minimizeWindow = () => {
|
const minimizeWindow = () => {
|
||||||
window.faWindowControlAPI.minimizeWindow()
|
console.log(process.env.MODE)
|
||||||
|
if (process.env.MODE === 'electron') {
|
||||||
|
window.faWindowControlAPI.minimizeWindow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Triggers resize of the window by the min/max button click
|
Triggers resize of the window by the min/max button click
|
||||||
*/
|
*/
|
||||||
const resizeWindow = () => {
|
const resizeWindow = () => {
|
||||||
window.faWindowControlAPI.resizeWindow()
|
if (process.env.MODE === 'electron') {
|
||||||
|
window.faWindowControlAPI.resizeWindow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -87,14 +93,18 @@ Otherwise, the app simply closes
|
||||||
*/
|
*/
|
||||||
const tryCloseWindow = () => {
|
const tryCloseWindow = () => {
|
||||||
// TODO add project close checking
|
// TODO add project close checking
|
||||||
window.faWindowControlAPI.closeWindow()
|
if (process.env.MODE === 'electron') {
|
||||||
|
window.faWindowControlAPI.closeWindow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Checks if the window is maximized and sets local ref
|
Checks if the window is maximized and sets local ref
|
||||||
*/
|
*/
|
||||||
const checkIfWindowMaximized = () => {
|
const checkIfWindowMaximized = () => {
|
||||||
isMaximized.value = window.faWindowControlAPI.checkWindowMaximized()
|
if (process.env.MODE === 'electron') {
|
||||||
|
isMaximized.value = window.faWindowControlAPI.checkWindowMaximized()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-btn
|
|
||||||
data-cy="button"
|
|
||||||
label="test emit"
|
|
||||||
color="positive"
|
|
||||||
rounded
|
|
||||||
icon="edit"
|
|
||||||
@click="$emit('test')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarButton',
|
|
||||||
emits: { test: () => true }
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,44 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-checkbox
|
|
||||||
v-model="checked"
|
|
||||||
data-cy="checkbox"
|
|
||||||
/>
|
|
||||||
<q-toggle
|
|
||||||
v-model="toggled"
|
|
||||||
data-cy="toggle"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<q-radio
|
|
||||||
v-model="selected"
|
|
||||||
val="Value1"
|
|
||||||
data-cy="radio-1"
|
|
||||||
>
|
|
||||||
Value1
|
|
||||||
</q-radio>
|
|
||||||
<q-radio
|
|
||||||
v-model="selected"
|
|
||||||
val="Value2"
|
|
||||||
data-cy="radio-2"
|
|
||||||
>
|
|
||||||
Value2
|
|
||||||
</q-radio>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, ref } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarCheckboxAndToggle',
|
|
||||||
setup () {
|
|
||||||
const checked = ref()
|
|
||||||
const toggled = ref()
|
|
||||||
const selected = ref()
|
|
||||||
|
|
||||||
return {
|
|
||||||
checked,
|
|
||||||
toggled,
|
|
||||||
selected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-card
|
|
||||||
data-cy="dark-card"
|
|
||||||
:dark="$q.dark.isActive"
|
|
||||||
>
|
|
||||||
{{ $q.dark.isActive ? 'Dark ' : 'Light' }} content
|
|
||||||
</q-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarDark'
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,50 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-date
|
|
||||||
v-model="date"
|
|
||||||
data-cy="date-picker"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<q-input
|
|
||||||
v-model="date"
|
|
||||||
label="Scegli data"
|
|
||||||
>
|
|
||||||
<template #append>
|
|
||||||
<q-btn
|
|
||||||
data-cy="open-date-picker-popup-button"
|
|
||||||
icon="event"
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
@click="dateDialogRef.show()"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<q-dialog ref="dateDialogRef">
|
|
||||||
<q-date
|
|
||||||
v-model="date"
|
|
||||||
@update:model-value="dateDialogRef.hide()"
|
|
||||||
/>
|
|
||||||
</q-dialog>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span data-cy="date-value">{{ date }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import type { QDialog } from 'quasar'
|
|
||||||
import type { Ref } from 'vue'
|
|
||||||
import { defineComponent, ref } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarDate',
|
|
||||||
setup () {
|
|
||||||
const date = ref('')
|
|
||||||
const dateDialogRef = ref() as Ref<QDialog>
|
|
||||||
|
|
||||||
return {
|
|
||||||
date,
|
|
||||||
dateDialogRef
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,78 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- notice dialogRef here -->
|
|
||||||
<q-dialog
|
|
||||||
ref="dialogRef"
|
|
||||||
@hide="onDialogHide"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>{{ message }}</q-card-section>
|
|
||||||
|
|
||||||
<!-- buttons example -->
|
|
||||||
<q-card-actions align="right">
|
|
||||||
<q-btn
|
|
||||||
data-cy="ok-button"
|
|
||||||
color="primary"
|
|
||||||
label="OK"
|
|
||||||
@click="onOKClick"
|
|
||||||
/>
|
|
||||||
<q-btn
|
|
||||||
color="primary"
|
|
||||||
label="Cancel"
|
|
||||||
@click="onCancelClick"
|
|
||||||
/>
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { useDialogPluginComponent } from 'quasar'
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarDialog',
|
|
||||||
props: {
|
|
||||||
message: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// REQUIRED; need to specify some events that your
|
|
||||||
// component will emit through useDialogPluginComponent()
|
|
||||||
emits: useDialogPluginComponent.emits,
|
|
||||||
|
|
||||||
setup () {
|
|
||||||
// REQUIRED; must be called inside of setup()
|
|
||||||
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
||||||
useDialogPluginComponent()
|
|
||||||
// dialogRef - Vue ref to be applied to QDialog
|
|
||||||
// onDialogHide - Function to be used as handler for @hide on QDialog
|
|
||||||
// onDialogOK - Function to call to settle dialog with "ok" outcome
|
|
||||||
// example: onDialogOK() - no payload
|
|
||||||
// example: onDialogOK({ /*.../* }) - with payload
|
|
||||||
// onDialogCancel - Function to call to settle dialog with "cancel" outcome
|
|
||||||
|
|
||||||
return {
|
|
||||||
// This is REQUIRED;
|
|
||||||
// Need to inject these (from useDialogPluginComponent() call)
|
|
||||||
// into the vue scope for the vue html template
|
|
||||||
dialogRef,
|
|
||||||
onDialogHide,
|
|
||||||
|
|
||||||
// other methods that we used in our vue html template;
|
|
||||||
// these are part of our example (so not required)
|
|
||||||
onOKClick () {
|
|
||||||
// on OK, it is REQUIRED to
|
|
||||||
// call onDialogOK (with optional payload)
|
|
||||||
onDialogOK()
|
|
||||||
// or with payload: onDialogOK({ ... })
|
|
||||||
// ...and it will also hide the dialog automatically
|
|
||||||
},
|
|
||||||
|
|
||||||
// we can passthrough onDialogCancel directly
|
|
||||||
onCancelClick: onDialogCancel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,40 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-drawer
|
|
||||||
v-model="showDrawer"
|
|
||||||
show-if-above
|
|
||||||
:width="200"
|
|
||||||
:breakpoint="700"
|
|
||||||
elevated
|
|
||||||
data-cy="drawer"
|
|
||||||
class="bg-primary text-white"
|
|
||||||
>
|
|
||||||
<q-scroll-area class="fit">
|
|
||||||
<div class="q-pa-sm">
|
|
||||||
<div
|
|
||||||
v-for="n in 50"
|
|
||||||
:key="n"
|
|
||||||
>
|
|
||||||
Drawer {{ n }} / 50
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-btn data-cy="button">
|
|
||||||
Am I on screen?
|
|
||||||
</q-btn>
|
|
||||||
</q-scroll-area>
|
|
||||||
</q-drawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { ref, defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarDrawer',
|
|
||||||
setup () {
|
|
||||||
const showDrawer = ref(true)
|
|
||||||
|
|
||||||
return {
|
|
||||||
showDrawer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,31 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-btn
|
|
||||||
data-cy="open-menu-btn"
|
|
||||||
label="Open menu"
|
|
||||||
>
|
|
||||||
<q-menu>
|
|
||||||
<q-list>
|
|
||||||
<q-item
|
|
||||||
v-close-popup
|
|
||||||
clickable
|
|
||||||
>
|
|
||||||
<q-item-section>Item 1</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item
|
|
||||||
v-close-popup
|
|
||||||
clickable
|
|
||||||
>
|
|
||||||
<q-item-section>Item 2</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-menu>
|
|
||||||
</q-btn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarMenu'
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-page-sticky
|
|
||||||
position="bottom-right"
|
|
||||||
:offset="[18, 18]"
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
data-cy="button"
|
|
||||||
rounded
|
|
||||||
color="accent"
|
|
||||||
icon="arrow_forward"
|
|
||||||
>
|
|
||||||
{{ title }}
|
|
||||||
</q-btn>
|
|
||||||
</q-page-sticky>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarPageSticky',
|
|
||||||
props: {
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,54 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-select
|
|
||||||
v-model="selected"
|
|
||||||
data-cy="select"
|
|
||||||
label="test options selection"
|
|
||||||
:options="options"
|
|
||||||
:loading="loading"
|
|
||||||
:disable="disable"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span data-cy="select-value">{{ selected }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, ref } from 'vue'
|
|
||||||
|
|
||||||
const syncOptions = ['Option 1', 'Option 2', 'Option 3']
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarSelect',
|
|
||||||
props: {
|
|
||||||
loadOptionsAsync: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
disable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup (props) {
|
|
||||||
const selected = ref()
|
|
||||||
const loading = ref(false)
|
|
||||||
|
|
||||||
const options = ref()
|
|
||||||
|
|
||||||
if (props.loadOptionsAsync) {
|
|
||||||
loading.value = true
|
|
||||||
setTimeout(() => {
|
|
||||||
options.value = syncOptions
|
|
||||||
loading.value = false
|
|
||||||
}, 2000)
|
|
||||||
} else {
|
|
||||||
options.value = syncOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
selected,
|
|
||||||
options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,31 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-btn
|
|
||||||
color="primary"
|
|
||||||
data-cy="button"
|
|
||||||
>
|
|
||||||
Button
|
|
||||||
<q-tooltip
|
|
||||||
v-model="showTooltip"
|
|
||||||
data-cy="tooltip"
|
|
||||||
class="bg-red"
|
|
||||||
:offset="[10, 10]"
|
|
||||||
>
|
|
||||||
Here I am!
|
|
||||||
</q-tooltip>
|
|
||||||
</q-btn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { ref, defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarTooltip',
|
|
||||||
setup () {
|
|
||||||
const showTooltip = ref(true)
|
|
||||||
|
|
||||||
return {
|
|
||||||
showTooltip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,32 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<span data-cy="model-value">{{ modelValue }}</span>
|
|
||||||
<button
|
|
||||||
data-cy="button"
|
|
||||||
@click="
|
|
||||||
$emit(
|
|
||||||
'update:modelValue',
|
|
||||||
modelValue.length > 0 ? modelValue.substring(1) : ''
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
Remove first letter
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'QuasarTooltip',
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
||||||
emits: { 'update:modelValue': (payload: string) => payload !== undefined }
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,43 +0,0 @@
|
||||||
import QuasarButton from '../QuasarButton.vue'
|
|
||||||
|
|
||||||
describe('QuasarButton', () => {
|
|
||||||
it('renders a message', () => {
|
|
||||||
const label = 'Hello there'
|
|
||||||
cy.mount(QuasarButton, {
|
|
||||||
props: {
|
|
||||||
label
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('button').should('contain', label)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('renders another message', () => {
|
|
||||||
const label = 'Will this work?'
|
|
||||||
cy.mount(QuasarButton, {
|
|
||||||
props: {
|
|
||||||
label
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('button').should('contain', label)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have a `positive` color', () => {
|
|
||||||
cy.mount(QuasarButton)
|
|
||||||
|
|
||||||
cy.dataCy('button')
|
|
||||||
.should('have.backgroundColor', 'var(--q-positive)')
|
|
||||||
.should('have.color', 'white')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should emit `test` upon click', () => {
|
|
||||||
cy.mount(QuasarButton)
|
|
||||||
|
|
||||||
cy.dataCy('button')
|
|
||||||
cy.dataCy('button').click()
|
|
||||||
cy.dataCy('button').should(() => {
|
|
||||||
expect(Cypress.vueWrapper.emitted('test')).to.have.length(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,38 +0,0 @@
|
||||||
import QuasarCheckComponents from '../QuasarCheckComponents.vue'
|
|
||||||
|
|
||||||
describe('QuasarCheckbox', () => {
|
|
||||||
it('can be used with normal Cypress commands', () => {
|
|
||||||
cy.mount(QuasarCheckComponents)
|
|
||||||
|
|
||||||
cy.dataCy('checkbox').check()
|
|
||||||
cy.dataCy('checkbox').should('be.checked')
|
|
||||||
|
|
||||||
cy.dataCy('checkbox').uncheck()
|
|
||||||
cy.dataCy('checkbox').should('not.be.checked')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('QuasarToggle', () => {
|
|
||||||
it('can be used with normal Cypress commands', () => {
|
|
||||||
cy.mount(QuasarCheckComponents)
|
|
||||||
|
|
||||||
cy.dataCy('toggle').check()
|
|
||||||
cy.dataCy('toggle').should('be.checked')
|
|
||||||
|
|
||||||
cy.dataCy('toggle').uncheck()
|
|
||||||
cy.dataCy('toggle').should('not.be.checked')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('QuasarToggle', () => {
|
|
||||||
it('can be used with normal Cypress commands', () => {
|
|
||||||
cy.mount(QuasarCheckComponents)
|
|
||||||
|
|
||||||
cy.dataCy('radio-1').check()
|
|
||||||
cy.dataCy('radio-1').should('be.checked')
|
|
||||||
|
|
||||||
cy.dataCy('radio-2').check()
|
|
||||||
cy.dataCy('radio-2').should('be.checked')
|
|
||||||
cy.dataCy('radio-1').should('not.be.checked')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { Dark } from 'quasar'
|
|
||||||
import QuasarDark from '../QuasarDark.vue'
|
|
||||||
|
|
||||||
describe('QuasarDark', () => {
|
|
||||||
it('changes its color', () => {
|
|
||||||
cy.mount(QuasarDark)
|
|
||||||
|
|
||||||
cy.dataCy('dark-card')
|
|
||||||
.should('not.have.class', 'q-dark')
|
|
||||||
.then(() => {
|
|
||||||
Dark.set(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('dark-card')
|
|
||||||
.should('have.class', 'q-dark')
|
|
||||||
.then(() => {
|
|
||||||
Cypress.vueWrapper.vm.$q.dark.set(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('dark-card').should('not.have.class', 'q-dark')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,33 +0,0 @@
|
||||||
import QuasarDate from '../QuasarDate.vue'
|
|
||||||
|
|
||||||
const targetDate = '2023/02/23'
|
|
||||||
|
|
||||||
describe('QuasarDate', () => {
|
|
||||||
it('selects a date by date string', () => {
|
|
||||||
cy.mount(QuasarDate)
|
|
||||||
|
|
||||||
cy.dataCy('date-picker').selectDate(targetDate)
|
|
||||||
cy.dataCy('date-value').should('have.text', targetDate)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selects a date by date object', () => {
|
|
||||||
cy.mount(QuasarDate)
|
|
||||||
|
|
||||||
cy.dataCy('date-picker').selectDate(new Date(targetDate))
|
|
||||||
cy.dataCy('date-value').should('have.text', targetDate)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selects a date displayed into a popup proxy', () => {
|
|
||||||
cy.mount(QuasarDate)
|
|
||||||
|
|
||||||
cy.dataCy('open-date-picker-popup-button').click()
|
|
||||||
cy.withinDialog(() => {
|
|
||||||
cy.get('.q-date').selectDate(targetDate)
|
|
||||||
})
|
|
||||||
cy.dataCy('date-value').should('have.text', targetDate)
|
|
||||||
|
|
||||||
// When dealing with a nested dialog, or a popup proxy within a dialog,
|
|
||||||
// add a data-cy on the dialog/popup-proxy containing the QDate and use the `withinDialog` extended signature:
|
|
||||||
// Example: cy.withinDialog({ dataCy: 'date-picker-popup', fn: () => { cy.get('.q-date').selectDate(targetDate); } })
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,42 +0,0 @@
|
||||||
import DialogWrapper from 'app/test/cypress/wrappers/DialogWrapper.vue'
|
|
||||||
import QuasarDialog from '../QuasarDialog.vue'
|
|
||||||
|
|
||||||
describe('QuasarDialog', () => {
|
|
||||||
it('should show a dialog with a message', () => {
|
|
||||||
const message = 'Hello, I am a dialog'
|
|
||||||
cy.mount(DialogWrapper, {
|
|
||||||
props: {
|
|
||||||
component: QuasarDialog,
|
|
||||||
componentProps: {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.withinDialog((el) => {
|
|
||||||
cy.wrap(el).should('contain', message)
|
|
||||||
cy.dataCy('ok-button').click()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should keep the dialog open when not dismissed', () => {
|
|
||||||
const message = 'Hello, I am a dialog'
|
|
||||||
cy.mount(DialogWrapper, {
|
|
||||||
props: {
|
|
||||||
component: QuasarDialog,
|
|
||||||
componentProps: {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// The helper won't check for the dialog to be closed
|
|
||||||
// when the callback completes
|
|
||||||
cy.withinDialog({
|
|
||||||
persistent: true,
|
|
||||||
fn: (el) => {
|
|
||||||
cy.wrap(el).should('contain', message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,20 +0,0 @@
|
||||||
import LayoutContainer from 'app/test/cypress/wrappers/LayoutContainer.vue'
|
|
||||||
import QuasarDrawer from '../QuasarDrawer.vue'
|
|
||||||
|
|
||||||
describe('QuasarDrawer', () => {
|
|
||||||
it('should show a drawer', () => {
|
|
||||||
cy.mount(LayoutContainer, {
|
|
||||||
props: {
|
|
||||||
component: QuasarDrawer
|
|
||||||
}
|
|
||||||
})
|
|
||||||
cy.dataCy('drawer')
|
|
||||||
.should('exist')
|
|
||||||
.dataCy('button')
|
|
||||||
.should('not.be.visible')
|
|
||||||
cy.get('.q-scrollarea .scroll')
|
|
||||||
cy.get('.q-scrollarea .scroll').scrollTo('bottom', { duration: 500 })
|
|
||||||
cy.get('.q-scrollarea .scroll').dataCy('button')
|
|
||||||
cy.get('.q-scrollarea .scroll').should('be.visible')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,21 +0,0 @@
|
||||||
import QuasarMenu from '../QuasarMenu.vue'
|
|
||||||
|
|
||||||
describe('QuasarMenu', () => {
|
|
||||||
it('click an item by content', () => {
|
|
||||||
cy.mount(QuasarMenu)
|
|
||||||
|
|
||||||
cy.dataCy('open-menu-btn').click()
|
|
||||||
cy.withinMenu(() => {
|
|
||||||
cy.get('.q-item').contains('Item 1').click()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('click an item by cardinality', () => {
|
|
||||||
cy.mount(QuasarMenu)
|
|
||||||
|
|
||||||
cy.dataCy('open-menu-btn').click()
|
|
||||||
cy.withinMenu(() => {
|
|
||||||
cy.get('.q-item').eq(1).click()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,21 +0,0 @@
|
||||||
import LayoutContainer from 'app/test/cypress/wrappers/LayoutContainer.vue'
|
|
||||||
import QuasarPageSticky from '../QuasarPageSticky.vue'
|
|
||||||
|
|
||||||
describe('QuasarPageSticky', () => {
|
|
||||||
it('should show a sticky at the bottom-right of the page', () => {
|
|
||||||
cy.mount(LayoutContainer, {
|
|
||||||
props: {
|
|
||||||
component: QuasarPageSticky,
|
|
||||||
title: 'Test'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('button')
|
|
||||||
.should('be.visible')
|
|
||||||
.should(($el) => {
|
|
||||||
const rect = $el[0].getBoundingClientRect()
|
|
||||||
expect(rect.bottom).to.equal(window.innerHeight - 18)
|
|
||||||
expect(rect.right).to.equal(window.innerWidth - 18)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,44 +0,0 @@
|
||||||
import QuasarSelect from '../QuasarSelect.vue'
|
|
||||||
|
|
||||||
function dataCySelect (dataCyId: string) {
|
|
||||||
return cy.dataCy(dataCyId).closest('.q-select')
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('QuasarSelect', () => {
|
|
||||||
it('makes sure the select is disabled', () => {
|
|
||||||
cy.mount(QuasarSelect, {
|
|
||||||
props: { disable: true }
|
|
||||||
})
|
|
||||||
|
|
||||||
// `cy.dataCy('select')` won't work in this case, as it won't get the root q-select element
|
|
||||||
dataCySelect('select').should('have.attr', 'aria-disabled', 'true')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selects an option by content', () => {
|
|
||||||
cy.mount(QuasarSelect)
|
|
||||||
|
|
||||||
cy.dataCy('select').select('Option 1')
|
|
||||||
cy.dataCy('select-value').should('have.text', 'Option 1')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selects an option by cardinality', () => {
|
|
||||||
cy.mount(QuasarSelect)
|
|
||||||
|
|
||||||
cy.dataCy('select').select(1)
|
|
||||||
cy.dataCy('select-value').should('have.text', 'Option 2')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selects an option asynchronously', () => {
|
|
||||||
cy.mount(QuasarSelect, {
|
|
||||||
props: {
|
|
||||||
loadOptionsAsync: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wait for loading to complete
|
|
||||||
cy.dataCy('select').get('.q-spinner').should('not.exist')
|
|
||||||
|
|
||||||
cy.dataCy('select').select('Option 3')
|
|
||||||
cy.dataCy('select-value').should('have.text', 'Option 3')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,10 +0,0 @@
|
||||||
import QuasarTooltip from '../QuasarTooltip.vue'
|
|
||||||
|
|
||||||
describe('QuasarTooltip', () => {
|
|
||||||
it('should show a tooltip', () => {
|
|
||||||
cy.mount(QuasarTooltip)
|
|
||||||
|
|
||||||
cy.dataCy('button').trigger('mouseover')
|
|
||||||
cy.dataCy('tooltip').contains('Here I am!')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { vModelAdapter } from '@quasar/quasar-app-extension-testing-e2e-cypress'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import VModelComponent from '../VModelComponent.vue'
|
|
||||||
|
|
||||||
describe('VModelComponent', () => {
|
|
||||||
it('should show the value', () => {
|
|
||||||
const text = 'Quasar'
|
|
||||||
|
|
||||||
cy.mount(VModelComponent, {
|
|
||||||
props: {
|
|
||||||
modelValue: text
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('model-value').should('contain', text)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should call the listener when an update via inner button occurs', () => {
|
|
||||||
const text = 'Quasar'
|
|
||||||
const fn = cy.stub()
|
|
||||||
|
|
||||||
cy.mount(VModelComponent, {
|
|
||||||
props: {
|
|
||||||
modelValue: text,
|
|
||||||
// This is how Vue internally codifies listeners,
|
|
||||||
// defining a prop prepended with `on` and camelCased
|
|
||||||
'onUpdate:modelValue': fn
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('button')
|
|
||||||
cy.dataCy('button').click()
|
|
||||||
cy.dataCy('button').then(() => {
|
|
||||||
expect(fn).to.be.calledWith('uasar')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update the value via inner button when not using the helper', () => {
|
|
||||||
const text = 'Quasar'
|
|
||||||
|
|
||||||
cy.mount(VModelComponent, {
|
|
||||||
props: {
|
|
||||||
modelValue: text,
|
|
||||||
'onUpdate:modelValue': (emittedValue: string) =>
|
|
||||||
Cypress.vueWrapper.setProps({ modelValue: emittedValue })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('button').click()
|
|
||||||
cy.dataCy('model-value').should('contain', 'uasar')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update the value via inner button using the helper', () => {
|
|
||||||
const model = ref('Quasar')
|
|
||||||
|
|
||||||
cy.mount(VModelComponent, {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
props: {
|
|
||||||
...vModelAdapter(model)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.dataCy('button').click()
|
|
||||||
cy.dataCy('model-value')
|
|
||||||
.should('contain', 'uasar')
|
|
||||||
.then(() => {
|
|
||||||
// You cannot access `model.value` in a synchronous way,
|
|
||||||
// you need to chain checks on it to a Cypress command or you'll be testing the initial value.
|
|
||||||
expect(model.value).to.equal('uasar')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,12 +0,0 @@
|
||||||
import ColorAssertionsComponent from '../color-assertions.vue'
|
|
||||||
|
|
||||||
describe('color assertions', () => {
|
|
||||||
it('works with names, hex codes and CSS variables', () => {
|
|
||||||
cy.mount(ColorAssertionsComponent)
|
|
||||||
|
|
||||||
cy.get('.wrapper')
|
|
||||||
.should('have.color', 'var(--q-primary)')
|
|
||||||
.and('have.backgroundColor', 'black')
|
|
||||||
.and('have.backgroundColor', '#000')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,16 +0,0 @@
|
||||||
import DataCyComponent from '../data-cy.vue'
|
|
||||||
|
|
||||||
describe('dataCy command', () => {
|
|
||||||
it('works as a parent command', () => {
|
|
||||||
cy.mount(DataCyComponent)
|
|
||||||
|
|
||||||
cy.dataCy('wrapper').should('exist')
|
|
||||||
cy.dataCy('paragraph').should('exist').and('contain', 'Test')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('works as a child command', () => {
|
|
||||||
cy.mount(DataCyComponent)
|
|
||||||
|
|
||||||
cy.dataCy('wrapper').dataCy('paragraph').should('exist')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,5 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="wrapper text-primary bg-black">
|
|
||||||
Text1
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<template>
|
|
||||||
<div data-cy="wrapper">
|
|
||||||
<p data-cy="paragraph">
|
|
||||||
Test
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { installQuasar } from '@quasar/quasar-app-extension-testing-unit-vitest'
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import { describe, expect, it } from 'vitest'
|
|
||||||
import ExampleComponent from './demo/ExampleComponent.vue'
|
|
||||||
|
|
||||||
installQuasar()
|
|
||||||
|
|
||||||
describe('example Component', () => {
|
|
||||||
it('should mount component with todos', () => {
|
|
||||||
const wrapper = mount(ExampleComponent, {
|
|
||||||
props: {
|
|
||||||
title: 'Hello',
|
|
||||||
meta: {
|
|
||||||
totalCount: 4
|
|
||||||
},
|
|
||||||
todos: [
|
|
||||||
{ id: 1, content: 'Hallo' },
|
|
||||||
{ id: 2, content: 'Hoi' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
expect(wrapper.vm.clickCount).toBe(0)
|
|
||||||
wrapper.find('.q-item').trigger('click')
|
|
||||||
expect(wrapper.vm.clickCount).toBe(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should mount component without todos', () => {
|
|
||||||
const wrapper = mount(ExampleComponent, {
|
|
||||||
props: {
|
|
||||||
title: 'Hello',
|
|
||||||
meta: {
|
|
||||||
totalCount: 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
expect(wrapper.findAll('.q-item')).toHaveLength(0)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { installQuasar } from '@quasar/quasar-app-extension-testing-unit-vitest'
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import { Notify } from 'quasar'
|
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
|
||||||
import NotifyComponent from './demo/NotifyComponent.vue'
|
|
||||||
|
|
||||||
installQuasar({ plugins: { Notify } })
|
|
||||||
|
|
||||||
describe('notify example', () => {
|
|
||||||
it('should call notify on click', async () => {
|
|
||||||
expect(NotifyComponent).toBeTruthy()
|
|
||||||
|
|
||||||
const wrapper = mount(NotifyComponent, {})
|
|
||||||
const spy = vi.spyOn(Notify, 'create')
|
|
||||||
expect(spy).not.toHaveBeenCalled()
|
|
||||||
wrapper.trigger('click')
|
|
||||||
expect(spy).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,52 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<p>{{ title }}</p>
|
|
||||||
<q-list>
|
|
||||||
<q-item
|
|
||||||
v-for="todo in todos"
|
|
||||||
:key="todo.id"
|
|
||||||
clickable
|
|
||||||
@click="increment"
|
|
||||||
>
|
|
||||||
{{ todo.id }} - {{ todo.content }}
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
|
|
||||||
<p>Count: {{ todoCount }} / {{ meta.totalCount }}</p>
|
|
||||||
<p>Active: {{ active ? 'yes' : 'no' }}</p>
|
|
||||||
<p>Clicks on todos: {{ clickCount }}</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, ref } from 'vue'
|
|
||||||
|
|
||||||
interface Todo {
|
|
||||||
id: number;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Meta {
|
|
||||||
totalCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
title: string;
|
|
||||||
todos?: Todo[];
|
|
||||||
meta: Meta;
|
|
||||||
active?: boolean;
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
todos: () => []
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const clickCount = ref(0)
|
|
||||||
function increment () {
|
|
||||||
clickCount.value += 1
|
|
||||||
return clickCount.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const todoCount = computed(() => props.todos.length)
|
|
||||||
</script>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-btn @click="onClick">
|
|
||||||
Click me!
|
|
||||||
</q-btn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { Notify } from 'quasar'
|
|
||||||
|
|
||||||
function onClick () {
|
|
||||||
Notify.create('Hello there!')
|
|
||||||
}
|
|
||||||
</script>
|
|
Loading…
Reference in a new issue