implemented cypress e2e testing
This commit is contained in:
parent
601cdc0908
commit
ecdb785abb
13
.eslintrc.js
13
.eslintrc.js
|
@ -65,5 +65,16 @@ module.exports = {
|
|||
|
||||
// allow debugger during development only
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
}
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.spec.{js,ts}'],
|
||||
extends: [
|
||||
// Add Cypress-specific lint rules, globals and Cypress plugin
|
||||
// See https://github.com/cypress-io/eslint-plugin-cypress#rules
|
||||
'plugin:cypress/recommended',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
@ -12,5 +12,13 @@
|
|||
"typescript",
|
||||
"vue"
|
||||
],
|
||||
"jest.jestCommandLine": "jest"
|
||||
"jest.jestCommandLine": "jest",
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"cypress.json"
|
||||
],
|
||||
"url": "https://on.cypress.io/cypress.schema.json"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:8080/",
|
||||
"fixturesFolder": "test/cypress/fixtures",
|
||||
"integrationFolder": "test/cypress/integration",
|
||||
"pluginsFile": "test/cypress/plugins/index.js",
|
||||
"screenshotsFolder": "test/cypress/screenshots",
|
||||
"supportFile": "test/cypress/support/index.js",
|
||||
"videosFolder": "test/cypress/videos",
|
||||
"video": true,
|
||||
"component": {
|
||||
"componentFolder": "src",
|
||||
"testFiles": "**/*.spec.js",
|
||||
"supportFile": "test/cypress/support/unit.js"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
|
@ -9,13 +9,13 @@
|
|||
"lint": "eslint --ext .js,.vue ./",
|
||||
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
||||
"test:unit": "jest --updateSnapshot",
|
||||
"test:unit": "jest --watchAll",
|
||||
"test:unit:ci": "jest --ci",
|
||||
"test:unit:coverage": "jest --coverage",
|
||||
"test:unit:watch": "jest --watch",
|
||||
"test:unit:watchAll": "jest --watchAll",
|
||||
"serve:test:coverage": "quasar serve test/jest/coverage/lcov-report/ --port 8788",
|
||||
"concurrently:dev:jest": "concurrently \"quasar dev\" \"jest --watch\""
|
||||
"concurrently:dev:jest": "concurrently \"quasar dev\" \"jest --watch\"",
|
||||
"test:e2e": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress open\"",
|
||||
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.0.0",
|
||||
|
@ -29,13 +29,15 @@
|
|||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.13.14",
|
||||
"@quasar/app-webpack": "^3.0.0",
|
||||
"@quasar/quasar-app-extension-testing-e2e-cypress": "^4.0.1",
|
||||
"@quasar/quasar-app-extension-testing-unit-jest": "^3.0.0-alpha.9",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-vue": "^8.5.0",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"prettier": "^2.5.1",
|
||||
"eslint-plugin-jest": "^25.2.2"
|
||||
"eslint-plugin-cypress": "^2.11.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 10 Chrome versions",
|
||||
|
|
|
@ -4,5 +4,10 @@
|
|||
"options": [
|
||||
"scripts"
|
||||
]
|
||||
},
|
||||
"@quasar/testing-e2e-cypress": {
|
||||
"options": [
|
||||
"scripts"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,5 +1,11 @@
|
|||
{
|
||||
"unit-jest": {
|
||||
"runnerCommand": "jest --ci"
|
||||
},
|
||||
"e2e-cypress": {
|
||||
"runnerCommand": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
|
||||
},
|
||||
"unit-cypress": {
|
||||
"runnerCommand": "cypress run-ct"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<q-btn
|
||||
data-cy="button"
|
||||
label="test emit"
|
||||
color="positive"
|
||||
rounded
|
||||
icon="edit"
|
||||
@click="$emit('test')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'QuasarButton',
|
||||
emits: ['test'],
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<!-- notice dialogRef here -->
|
||||
<q-dialog ref="dialogRef" @hide="onDialogHide" data-cy="dialog">
|
||||
<q-card class="q-dialog-plugin">
|
||||
<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>
|
||||
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>
|
|
@ -0,0 +1,33 @@
|
|||
<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>
|
||||
import { ref, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'QuasarDrawer',
|
||||
setup() {
|
||||
const showDrawer = ref(true);
|
||||
|
||||
return {
|
||||
showDrawer,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,21 @@
|
|||
<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>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'QuasarPageSticky',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,28 @@
|
|||
<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>
|
||||
import { ref, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'QuasarTooltip',
|
||||
setup() {
|
||||
const showTooltip = ref(true);
|
||||
|
||||
return {
|
||||
showTooltip,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,44 @@
|
|||
import { mount } from '@cypress/vue';
|
||||
import QuasarButton from '../QuasarButton.vue';
|
||||
|
||||
describe('QuasarButton', () => {
|
||||
it('renders a message', () => {
|
||||
const label = 'Hello there';
|
||||
mount(QuasarButton, {
|
||||
props: {
|
||||
label,
|
||||
},
|
||||
});
|
||||
|
||||
cy.dataCy('button').should('contain', label);
|
||||
});
|
||||
|
||||
it('renders another message', () => {
|
||||
const label = 'Will this work?';
|
||||
mount(QuasarButton, {
|
||||
props: {
|
||||
label,
|
||||
},
|
||||
});
|
||||
|
||||
cy.dataCy('button').should('contain', label);
|
||||
});
|
||||
|
||||
it('should have a `positive` color', () => {
|
||||
mount(QuasarButton);
|
||||
|
||||
cy.dataCy('button')
|
||||
.should('have.backgroundColor', 'var(--q-positive)')
|
||||
.should('have.color', 'white');
|
||||
});
|
||||
|
||||
it('should emit `test` upon click', () => {
|
||||
mount(QuasarButton);
|
||||
|
||||
cy.dataCy('button')
|
||||
.click()
|
||||
.should(() => {
|
||||
expect(Cypress.vueWrapper.emitted('test')).to.have.length(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import { mount } from '@cypress/vue';
|
||||
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';
|
||||
mount(DialogWrapper, {
|
||||
props: {
|
||||
component: QuasarDialog,
|
||||
componentProps: {
|
||||
message,
|
||||
},
|
||||
},
|
||||
});
|
||||
cy.dataCy('dialog').should('exist').should('contain', message);
|
||||
});
|
||||
|
||||
it('should close a dialog when clikcing ok', () => {
|
||||
// The dialog is still visible from the previous test
|
||||
cy.dataCy('dialog').should('exist').dataCy('ok-button').click();
|
||||
cy.dataCy('dialog').should('not.exist');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import { mount } from '@cypress/vue';
|
||||
import LayoutContainer from 'app/test/cypress/wrappers/LayoutContainer.vue';
|
||||
import QuasarDrawer from '../QuasarDrawer.vue';
|
||||
|
||||
describe('QuasarDrawer', () => {
|
||||
it('should show a drawer', () => {
|
||||
mount(LayoutContainer, {
|
||||
props: {
|
||||
component: QuasarDrawer,
|
||||
},
|
||||
});
|
||||
cy.dataCy('drawer')
|
||||
.should('exist')
|
||||
.dataCy('button')
|
||||
.should('not.be.visible');
|
||||
cy.get('.q-scrollarea .scroll')
|
||||
.scrollTo('bottom', { duration: 500 })
|
||||
.dataCy('button')
|
||||
.should('be.visible');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
import { mount } from '@cypress/vue';
|
||||
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', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import { mount } from '@cypress/vue';
|
||||
import QuasarTooltip from '../QuasarTooltip.vue';
|
||||
|
||||
describe('QuasarTooltip', () => {
|
||||
it('should show a tooltip', () => {
|
||||
mount(QuasarTooltip);
|
||||
|
||||
cy.dataCy('button').trigger('mouseover');
|
||||
cy.dataCy('tooltip').contains('Here I am!');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
videos/*
|
||||
screenshots/*
|
|
@ -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"
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/// <reference types="cypress" />
|
||||
// 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', 'Quasar');
|
||||
});
|
||||
});
|
||||
|
||||
// ** 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');
|
||||
// });
|
||||
// });
|
|
@ -0,0 +1,31 @@
|
|||
/// <reference types="cypress" />
|
||||
/* eslint-env node */
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
// cypress/plugins/index.js
|
||||
|
||||
// const {injectDevServer} = require('@quasar/quasar-app-extension-testing-e2e-cypress/cct-dev-server');
|
||||
|
||||
// /**
|
||||
// * @type {Cypress.PluginConfig}
|
||||
// */
|
||||
module.exports = async (on, config) => {
|
||||
// // Enable component testing, you can safely remove this
|
||||
// // if you don't plan to use Cypress for unit tests
|
||||
// if (config.testingType === 'component') {
|
||||
// await injectDevServer(on, config);
|
||||
// }
|
||||
|
||||
return config;
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
// ***********************************************
|
||||
// This example commands.js 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 is 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();
|
|
@ -0,0 +1,16 @@
|
|||
// ***********************************************************
|
||||
// This example support/index.js 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';
|
|
@ -0,0 +1,46 @@
|
|||
// ***********************************************************
|
||||
// This example support/unit.js is processed and
|
||||
// loaded automatically before your unit 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';
|
||||
|
||||
// Change this if you have a different entrypoint for the main scss.
|
||||
import 'src/css/app.scss';
|
||||
// Quasar styles
|
||||
import 'quasar/src/css/index.sass';
|
||||
|
||||
// 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 { config } from '@vue/test-utils';
|
||||
import { Dialog } from 'quasar';
|
||||
|
||||
// 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 } });
|
|
@ -0,0 +1,26 @@
|
|||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import { Dialog } from 'quasar';
|
||||
|
||||
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>
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<component :is="component" v-bind="$attrs" />
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutContainer',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
component: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue