mirror of
https://github.com/Elvanos/fantasia-archive.git
synced 2024-05-04 04:13:34 +12:00
First commit with working test of all types
This commit is contained in:
parent
a3f637d55a
commit
a50671c3c1
|
@ -9,7 +9,7 @@ module.exports = {
|
|||
// `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted
|
||||
parserOptions: {
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
extraFileExtensions: [ '.vue' ]
|
||||
extraFileExtensions: ['.vue']
|
||||
},
|
||||
|
||||
env: {
|
||||
|
@ -32,11 +32,12 @@ module.exports = {
|
|||
// but leave only one uncommented!
|
||||
// See https://eslint.vuejs.org/rules/#available-rules
|
||||
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
||||
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||
'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||
'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||
|
||||
'standard',
|
||||
'plugin:cypress/recommended'
|
||||
|
||||
'standard'
|
||||
|
||||
],
|
||||
|
||||
plugins: [
|
||||
|
@ -46,7 +47,7 @@ module.exports = {
|
|||
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
||||
// required to lint *.vue files
|
||||
'vue'
|
||||
|
||||
|
||||
],
|
||||
|
||||
globals: {
|
||||
|
@ -64,7 +65,7 @@ module.exports = {
|
|||
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
|
||||
|
||||
// allow async-await
|
||||
'generator-star-spacing': 'off',
|
||||
// allow paren-less arrow functions
|
||||
|
@ -84,7 +85,7 @@ module.exports = {
|
|||
// The core 'import/named' rules
|
||||
// does not work with type definitions
|
||||
'import/named': 'off',
|
||||
|
||||
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
|
||||
quotes: ['warn', 'single', { avoidEscape: true }],
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -35,3 +35,6 @@ yarn-error.log*
|
|||
|
||||
# local .env files
|
||||
.env.local*
|
||||
|
||||
.nyc_output
|
||||
coverage/
|
||||
|
|
3
.nycrc
Normal file
3
.nycrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "@quasar/quasar-app-extension-testing-e2e-cypress/nyc-config-preset"
|
||||
}
|
23
README.md
23
README.md
|
@ -4,6 +4,8 @@ A worldbuilding database manager
|
|||
|
||||
Use Yarn 1.22.19 or stuff is gonna bug out.
|
||||
|
||||
Make sure you are running this with Node v16.17.0 ("nvm" is great for these older versions)
|
||||
|
||||
## Install the dependencies and set up the project
|
||||
```bash
|
||||
yarn
|
||||
|
@ -14,7 +16,7 @@ yarn
|
|||
quasar dev -m electron
|
||||
```
|
||||
|
||||
### Lint the files
|
||||
### Lint the files manually (you can do this, but like... use an plugin of some kind in your IDE please T_T)
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
@ -24,5 +26,24 @@ yarn lint
|
|||
quasar build
|
||||
```
|
||||
|
||||
### Testing:
|
||||
|
||||
##### Unit test - with pretty web-UI
|
||||
```bash
|
||||
test:unit:ui
|
||||
```
|
||||
##### 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)
|
||||
```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)
|
||||
```bash
|
||||
test:e2e
|
||||
```
|
||||
|
||||
### Customize the configuration
|
||||
See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).
|
||||
|
|
29
cypress.config.ts
Normal file
29
cypress.config.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import registerCodeCoverageTasks from '@cypress/code-coverage/task';
|
||||
import { injectQuasarDevServerConfig } from '@quasar/quasar-app-extension-testing-e2e-cypress/cct-dev-server';
|
||||
import { defineConfig } from 'cypress';
|
||||
|
||||
export default defineConfig({
|
||||
fixturesFolder: 'test/cypress/fixtures',
|
||||
screenshotsFolder: 'test/cypress/screenshots',
|
||||
videosFolder: 'test/cypress/videos',
|
||||
video: true,
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
registerCodeCoverageTasks(on, config);
|
||||
return config;
|
||||
},
|
||||
baseUrl: 'http://localhost:9000/',
|
||||
supportFile: 'test/cypress/support/e2e.ts',
|
||||
specPattern: 'test/cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
|
||||
},
|
||||
component: {
|
||||
setupNodeEvents(on, config) {
|
||||
registerCodeCoverageTasks(on, config);
|
||||
return config;
|
||||
},
|
||||
supportFile: 'test/cypress/support/component.ts',
|
||||
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
||||
indexHtmlFile: 'test/cypress/support/component-index.html',
|
||||
devServer: injectQuasarDevServerConfig(),
|
||||
},
|
||||
});
|
22
package.json
22
package.json
|
@ -7,9 +7,16 @@
|
|||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.ts,.vue ./",
|
||||
"test": "echo \"No test specified\" && exit 0",
|
||||
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
||||
"dev": "quasar dev",
|
||||
"build": "quasar build"
|
||||
"build": "quasar build",
|
||||
"test:unit:ui": "vitest --ui",
|
||||
"test:unit": "vitest",
|
||||
"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: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:ci": "cross-env NODE_ENV=test cypress run --component"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.10",
|
||||
|
@ -24,18 +31,27 @@
|
|||
"devDependencies": {
|
||||
"@intlify/vite-plugin-vue-i18n": "^3.3.1",
|
||||
"@quasar/app-vite": "^1.3.0",
|
||||
"@quasar/quasar-app-extension-testing": "^2.1.0",
|
||||
"@quasar/quasar-app-extension-testing-e2e-cypress": "^5.1.0",
|
||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.1.0",
|
||||
"@types/node": "^12.20.21",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"@vitest/ui": "^0.15.0",
|
||||
"@vue/test-utils": "^2.0.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"cypress": "^12.2.0",
|
||||
"electron": "^25.5.0",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-import": "^2.19.1",
|
||||
"eslint-plugin-n": "^15.0.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-vue": "^9.0.0",
|
||||
"typescript": "^4.5.4"
|
||||
"jsdom": "^22.1.0",
|
||||
"typescript": "^4.5.4",
|
||||
"vitest": "^0.15.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || ^16 || ^14.19",
|
||||
|
|
22
quasar.extensions.json
Normal file
22
quasar.extensions.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"@quasar/testing": {
|
||||
"harnesses": [
|
||||
"unit-vitest@alpha",
|
||||
"e2e-cypress"
|
||||
]
|
||||
},
|
||||
"@quasar/testing-unit-vitest": {
|
||||
"options": [
|
||||
"scripts",
|
||||
"typescript",
|
||||
"ui"
|
||||
]
|
||||
},
|
||||
"@quasar/testing-e2e-cypress": {
|
||||
"options": [
|
||||
"scripts",
|
||||
"typescript",
|
||||
"code-coverage"
|
||||
]
|
||||
}
|
||||
}
|
11
quasar.testing.json
Normal file
11
quasar.testing.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"unit-vitest": {
|
||||
"runnerCommand": "vitest run"
|
||||
},
|
||||
"e2e-cypress": {
|
||||
"runnerCommand": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress run --e2e\""
|
||||
},
|
||||
"component-cypress": {
|
||||
"runnerCommand": "cross-env NODE_ENV=test cypress run --component"
|
||||
}
|
||||
}
|
|
@ -14,7 +14,9 @@
|
|||
|
||||
<q-item-section>
|
||||
<q-item-label>{{ title }}</q-item-label>
|
||||
<q-item-label caption>{{ caption }}</q-item-label>
|
||||
<q-item-label caption>
|
||||
{{ caption }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
<div>
|
||||
<p>{{ title }}</p>
|
||||
<ul>
|
||||
<li v-for="todo in todos" :key="todo.id" @click="increment">
|
||||
<li
|
||||
v-for="todo in todos"
|
||||
:key="todo.id"
|
||||
@click="increment"
|
||||
>
|
||||
{{ todo.id }} - {{ todo.content }}
|
||||
</li>
|
||||
</ul>
|
||||
|
|
132
src/components/GlobalWindowButtons/GlobalWindowButtons.vue
Normal file
132
src/components/GlobalWindowButtons/GlobalWindowButtons.vue
Normal file
|
@ -0,0 +1,132 @@
|
|||
<template>
|
||||
|
||||
<q-btn-group
|
||||
flat
|
||||
class="appWindowButtons bg-dark"
|
||||
>
|
||||
|
||||
<!-- Minimize button-->
|
||||
<q-btn
|
||||
flat
|
||||
:ripple="false"
|
||||
:class="{'minimize': osSystem === 'darwin'}"
|
||||
dark
|
||||
size='sm'
|
||||
@click="minimizeWindow">
|
||||
<q-icon name="mdi-window-minimize"></q-icon>
|
||||
</q-btn>
|
||||
|
||||
<!-- MinMax button-->
|
||||
<q-btn
|
||||
flat
|
||||
:ripple="false"
|
||||
:class="{'minMax': osSystem === 'darwin'}"
|
||||
dark
|
||||
size='sm'
|
||||
>
|
||||
<q-icon :name="(isMaximized)? 'mdi-window-restore' : 'mdi-window-maximize'"></q-icon>
|
||||
</q-btn>
|
||||
|
||||
<!-- Close button-->
|
||||
<q-btn
|
||||
flat
|
||||
:ripple="false"
|
||||
dark
|
||||
size='sm'
|
||||
:class="[{'close': osSystem === 'darwin'}]"
|
||||
>
|
||||
<q-icon name="mdi-window-close"></q-icon>
|
||||
</q-btn>
|
||||
|
||||
</q-btn-group>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
export default {
|
||||
/****************************************************************/
|
||||
// Basic component functionality
|
||||
/****************************************************************/
|
||||
|
||||
/**
|
||||
* Determines if the window is maximed or not
|
||||
*/
|
||||
isMaximized = false
|
||||
|
||||
/**
|
||||
* Gets the currently used OS
|
||||
*/
|
||||
osSystem = remote.process.platform
|
||||
|
||||
/**
|
||||
* Currently opened window
|
||||
*/
|
||||
currentWindow = remote.getCurrentWindow()
|
||||
|
||||
/**
|
||||
* Checks if the window is currently maximized or not
|
||||
*/
|
||||
checkIfMaximized () {
|
||||
this.isMaximized = this.currentWindow.isMaximized()
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimizes the current window
|
||||
*/
|
||||
minimizeWindow () {
|
||||
this.currentWindow.minimize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the window to either smaller or maximized
|
||||
*/
|
||||
resizeWindow () {
|
||||
if (this.currentWindow.isMaximized()) {
|
||||
this.currentWindow.unmaximize()
|
||||
}
|
||||
else {
|
||||
this.currentWindow.maximize()
|
||||
}
|
||||
}
|
||||
|
||||
created () {
|
||||
window.addEventListener("resize", this.checkIfMaximized)
|
||||
this.checkIfMaximized()
|
||||
}
|
||||
|
||||
destroyed () {
|
||||
window.addEventListener("resize", this.checkIfMaximized)
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
// Close project dialog
|
||||
/****************************************************************/
|
||||
|
||||
projectCloseCheckDialogTrigger: string | false = false
|
||||
projectCloseCheckDialogClose () {
|
||||
this.projectCloseCheckDialogTrigger = false
|
||||
}
|
||||
|
||||
projectCloseCheckDialogAssignUID () {
|
||||
this.projectCloseCheckDialogTrigger = this.generateUID()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.appWindowButtons {
|
||||
border-radius: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 40px;
|
||||
z-index: 99999999;
|
||||
color: #fff;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" >
|
||||
|
||||
</style>
|
19
src/components/QuasarButton.vue
Normal file
19
src/components/QuasarButton.vue
Normal file
|
@ -0,0 +1,19 @@
|
|||
<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>
|
44
src/components/QuasarCheckComponents.vue
Normal file
44
src/components/QuasarCheckComponents.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<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>
|
16
src/components/QuasarDark.vue
Normal file
16
src/components/QuasarDark.vue
Normal file
|
@ -0,0 +1,16 @@
|
|||
<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>
|
50
src/components/QuasarDate.vue
Normal file
50
src/components/QuasarDate.vue
Normal file
|
@ -0,0 +1,50 @@
|
|||
<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>
|
78
src/components/QuasarDialog.vue
Normal file
78
src/components/QuasarDialog.vue
Normal file
|
@ -0,0 +1,78 @@
|
|||
<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>
|
40
src/components/QuasarDrawer.vue
Normal file
40
src/components/QuasarDrawer.vue
Normal file
|
@ -0,0 +1,40 @@
|
|||
<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>
|
31
src/components/QuasarMenu.vue
Normal file
31
src/components/QuasarMenu.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<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>
|
29
src/components/QuasarPageSticky.vue
Normal file
29
src/components/QuasarPageSticky.vue
Normal file
|
@ -0,0 +1,29 @@
|
|||
<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>
|
54
src/components/QuasarSelect.vue
Normal file
54
src/components/QuasarSelect.vue
Normal file
|
@ -0,0 +1,54 @@
|
|||
<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>
|
31
src/components/QuasarTooltip.vue
Normal file
31
src/components/QuasarTooltip.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<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>
|
32
src/components/VModelComponent.vue
Normal file
32
src/components/VModelComponent.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<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>
|
43
src/components/__tests__/QuasarButton.cy.ts
Normal file
43
src/components/__tests__/QuasarButton.cy.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
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)
|
||||
})
|
||||
})
|
||||
})
|
38
src/components/__tests__/QuasarCheckComponents.cy.ts
Normal file
38
src/components/__tests__/QuasarCheckComponents.cy.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
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')
|
||||
})
|
||||
})
|
22
src/components/__tests__/QuasarDark.cy.ts
Normal file
22
src/components/__tests__/QuasarDark.cy.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
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')
|
||||
})
|
||||
})
|
33
src/components/__tests__/QuasarDate.cy.ts
Normal file
33
src/components/__tests__/QuasarDate.cy.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
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); } })
|
||||
})
|
||||
})
|
42
src/components/__tests__/QuasarDialog.cy.ts
Normal file
42
src/components/__tests__/QuasarDialog.cy.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
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)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
20
src/components/__tests__/QuasarDrawer.cy.ts
Normal file
20
src/components/__tests__/QuasarDrawer.cy.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
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')
|
||||
})
|
||||
})
|
21
src/components/__tests__/QuasarMenu.cy.ts
Normal file
21
src/components/__tests__/QuasarMenu.cy.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
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()
|
||||
})
|
||||
})
|
||||
})
|
21
src/components/__tests__/QuasarPageSticky.cy.ts
Normal file
21
src/components/__tests__/QuasarPageSticky.cy.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
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)
|
||||
})
|
||||
})
|
||||
})
|
44
src/components/__tests__/QuasarSelect.cy.ts
Normal file
44
src/components/__tests__/QuasarSelect.cy.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
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')
|
||||
})
|
||||
})
|
10
src/components/__tests__/QuasarTooltip.cy.ts
Normal file
10
src/components/__tests__/QuasarTooltip.cy.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
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!')
|
||||
})
|
||||
})
|
72
src/components/__tests__/VModelComponent.cy.ts
Normal file
72
src/components/__tests__/VModelComponent.cy.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
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')
|
||||
})
|
||||
})
|
||||
})
|
12
src/components/__tests__/color-assertions.cy.ts
Normal file
12
src/components/__tests__/color-assertions.cy.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
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')
|
||||
})
|
||||
})
|
16
src/components/__tests__/data-cy.cy.ts
Normal file
16
src/components/__tests__/data-cy.cy.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
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')
|
||||
})
|
||||
})
|
5
src/components/color-assertions.vue
Normal file
5
src/components/color-assertions.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<div class="wrapper text-primary bg-black">
|
||||
Text1
|
||||
</div>
|
||||
</template>
|
7
src/components/data-cy.vue
Normal file
7
src/components/data-cy.vue
Normal file
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<div data-cy="wrapper">
|
||||
<p data-cy="paragraph">
|
||||
Test
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
2
test/cypress/.gitignore
vendored
Normal file
2
test/cypress/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
videos/*
|
||||
screenshots/*
|
46
test/cypress/e2e/home.cy.ts
Normal file
46
test/cypress/e2e/home.cy.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Use `cy.dataCy` custom command for more robust tests
|
||||
// See https://docs.cypress.io/guides/references/best-practices.html#Selecting-Elements
|
||||
|
||||
// ** This file is an example of how to write Cypress tests, you can safely delete it **
|
||||
|
||||
// This test will pass when run against a clean Quasar project
|
||||
describe('Landing', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/')
|
||||
})
|
||||
it('.should() - assert that <title> is correct', () => {
|
||||
cy.title().should('include', 'Fantasia Archive')
|
||||
cy.get('li').first().click()
|
||||
cy.contains('Clicks on todos: 1').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
// ** The following code is an example to show you how to write some tests for your home page **
|
||||
//
|
||||
// describe('Home page tests', () => {
|
||||
// beforeEach(() => {
|
||||
// cy.visit('/');
|
||||
// });
|
||||
// it('has pretty background', () => {
|
||||
// cy.dataCy('landing-wrapper')
|
||||
// .should('have.css', 'background')
|
||||
// .and('match', /(".+(\/img\/background).+\.png)/);
|
||||
// });
|
||||
// it('has pretty logo', () => {
|
||||
// cy.dataCy('landing-wrapper img')
|
||||
// .should('have.class', 'logo-main')
|
||||
// .and('have.attr', 'src')
|
||||
// .and('match', /^(data:image\/svg\+xml).+/);
|
||||
// });
|
||||
// it('has very important information', () => {
|
||||
// cy.dataCy('instruction-wrapper')
|
||||
// .should('contain', 'SETUP INSTRUCTIONS')
|
||||
// .and('contain', 'Configure Authentication')
|
||||
// .and('contain', 'Database Configuration and CRUD operations')
|
||||
// .and('contain', 'Continuous Integration & Continuous Deployment CI/CD');
|
||||
// });
|
||||
// });
|
||||
|
||||
// Workaround for Cypress AE + TS + Vite
|
||||
// See: https://github.com/quasarframework/quasar-testing/issues/262#issuecomment-1154127497
|
||||
export {}
|
5
test/cypress/fixtures/example.json
Normal file
5
test/cypress/fixtures/example.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
30
test/cypress/support/commands.ts
Normal file
30
test/cypress/support/commands.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
// ***********************************************
|
||||
// This example commands.ts shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
// DO NOT REMOVE
|
||||
// Imports Quasar Cypress AE predefined commands
|
||||
import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress'
|
||||
registerCommands()
|
12
test/cypress/support/component-index.html
Normal file
12
test/cypress/support/component-index.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Components App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-cy-root></div>
|
||||
</body>
|
||||
</html>
|
51
test/cypress/support/component.ts
Normal file
51
test/cypress/support/component.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
// ***********************************************************
|
||||
// This example support/component.ts is processed and
|
||||
// loaded automatically before your component test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'component.supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
import './commands'
|
||||
import '@cypress/code-coverage/support'
|
||||
|
||||
// Quasar styles
|
||||
import 'quasar/src/css/index.sass' // Or 'quasar/dist/quasar.prod.css' if no CSS preprocessor is installed
|
||||
// Change this if you have a different entrypoint for the main scss.
|
||||
import 'src/css/app.scss' // Or 'src/css/app.css' if no CSS preprocessor is installed
|
||||
|
||||
// ICON SETS
|
||||
// If you use multiple or different icon-sets then the default, be sure to import them here.
|
||||
import 'quasar/dist/icon-set/material-icons.umd.prod'
|
||||
import '@quasar/extras/material-icons/material-icons.css'
|
||||
|
||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-e2e-cypress'
|
||||
import { Dialog } from 'quasar'
|
||||
|
||||
// Since Cypress v10 we cannot import `config` directly from VTU as Cypress bundles its own version of it
|
||||
// See https://github.com/cypress-io/cypress/issues/22611
|
||||
import { VueTestUtils } from 'cypress/vue'
|
||||
const { config } = VueTestUtils
|
||||
|
||||
// Example to import i18n from boot and use as plugin
|
||||
// import { i18n } from 'src/boot/i18n';
|
||||
|
||||
// You can modify the global config here for all tests or pass in the configuration per test
|
||||
// For example use the actual i18n instance or mock it
|
||||
// config.global.plugins.push(i18n);
|
||||
config.global.mocks = {
|
||||
$t: () => ''
|
||||
}
|
||||
|
||||
// Overwrite the transition and transition-group stubs which are stubbed by test-utils by default.
|
||||
// We do want transitions to show when doing visual testing :)
|
||||
config.global.stubs = {}
|
||||
|
||||
installQuasarPlugin({ plugins: { Dialog } })
|
17
test/cypress/support/e2e.ts
Normal file
17
test/cypress/support/e2e.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// ***********************************************************
|
||||
// This example support/e2e.ts is processed and
|
||||
// loaded automatically before your e2e test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
import './commands'
|
||||
import '@cypress/code-coverage/support'
|
26
test/cypress/wrappers/DialogWrapper.vue
Normal file
26
test/cypress/wrappers/DialogWrapper.vue
Normal file
|
@ -0,0 +1,26 @@
|
|||
<script lang="ts">
|
||||
import { Dialog } from 'quasar'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DialogWrapper',
|
||||
props: {
|
||||
component: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
componentProps: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
Dialog.create({
|
||||
component: props.component,
|
||||
|
||||
// props forwarded to your custom component
|
||||
componentProps: props.componentProps
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
20
test/cypress/wrappers/LayoutContainer.vue
Normal file
20
test/cypress/wrappers/LayoutContainer.vue
Normal file
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<component :is="component" v-bind="$attrs" />
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutContainer',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
component: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
38
test/vitest/__tests__/ExampleComponent.test.ts
Normal file
38
test/vitest/__tests__/ExampleComponent.test.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
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)
|
||||
})
|
||||
})
|
19
test/vitest/__tests__/NotifyComponent.test.ts
Normal file
19
test/vitest/__tests__/NotifyComponent.test.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
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()
|
||||
})
|
||||
})
|
52
test/vitest/__tests__/demo/ExampleComponent.vue
Normal file
52
test/vitest/__tests__/demo/ExampleComponent.vue
Normal file
|
@ -0,0 +1,52 @@
|
|||
<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>
|
13
test/vitest/__tests__/demo/NotifyComponent.vue
Normal file
13
test/vitest/__tests__/demo/NotifyComponent.vue
Normal file
|
@ -0,0 +1,13 @@
|
|||
<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>
|
1
test/vitest/setup-file.ts
Normal file
1
test/vitest/setup-file.ts
Normal file
|
@ -0,0 +1 @@
|
|||
// This file will be run before each test file
|
27
vitest.config.ts
Normal file
27
vitest.config.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { defineConfig } from 'vitest/config'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
setupFiles: 'test/vitest/setup-file.ts',
|
||||
include: [
|
||||
// Matches vitest tests in any subfolder of 'src' or into 'test/vitest/__tests__'
|
||||
// Matches all files with extension 'js', 'jsx', 'ts' and 'tsx'
|
||||
'src/**/*.vitest.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}',
|
||||
'test/vitest/__tests__/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
vue({
|
||||
template: { transformAssetUrls }
|
||||
}),
|
||||
quasar({
|
||||
sassVariables: 'src/quasar-variables.scss'
|
||||
}),
|
||||
tsconfigPaths()
|
||||
]
|
||||
})
|
Loading…
Reference in a new issue