Removed Cypres, added proper Playwright tests

This commit is contained in:
Elvanos 2023-09-03 20:36:10 +02:00
parent 88faa7a506
commit 9033c02ef7
23 changed files with 168 additions and 1830 deletions

View file

@ -8,15 +8,15 @@ module.exports = {
// Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working
// `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']
parser: require.resolve("@typescript-eslint/parser"),
extraFileExtensions: [".vue"],
},
env: {
browser: true,
es2021: true,
node: true,
'vue/setup-compiler-macros': true
"vue/setup-compiler-macros": true,
},
// Rules order is important, please avoid shuffling them
@ -26,86 +26,82 @@ module.exports = {
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
// ESLint typescript rules
'plugin:@typescript-eslint/recommended',
"plugin:@typescript-eslint/recommended",
// Uncomment any of the lines below to choose desired strictness,
// 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)
'standard',
'plugin:cypress/recommended'
"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)
"standard",
],
plugins: [
// required to apply rules which need type information
'@typescript-eslint',
"@typescript-eslint",
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
// required to lint *.vue files
'vue'
"vue",
],
globals: {
ga: 'readonly', // Google Analytics
cordova: 'readonly',
__statics: 'readonly',
__QUASAR_SSR__: 'readonly',
__QUASAR_SSR_SERVER__: 'readonly',
__QUASAR_SSR_CLIENT__: 'readonly',
__QUASAR_SSR_PWA__: 'readonly',
process: 'readonly',
Capacitor: 'readonly',
chrome: 'readonly'
ga: "readonly", // Google Analytics
cordova: "readonly",
__statics: "readonly",
__QUASAR_SSR__: "readonly",
__QUASAR_SSR_SERVER__: "readonly",
__QUASAR_SSR_CLIENT__: "readonly",
__QUASAR_SSR_PWA__: "readonly",
process: "readonly",
Capacitor: "readonly",
chrome: "readonly",
},
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
"generator-star-spacing": "off",
// allow paren-less arrow functions
'arrow-parens': 'off',
'one-var': 'off',
'no-void': 'off',
'multiline-ternary': 'off',
"arrow-parens": "off",
"one-var": "off",
"no-void": "off",
"multiline-ternary": "off",
'import/first': 'off',
'import/namespace': 'error',
'import/default': 'error',
'import/export': 'error',
'import/extensions': 'off',
'import/no-unresolved': 'off',
'import/no-extraneous-dependencies': 'off',
"import/first": "off",
"import/namespace": "error",
"import/default": "error",
"import/export": "error",
"import/extensions": "off",
"import/no-unresolved": "off",
"import/no-extraneous-dependencies": "off",
// The core 'import/named' rules
// does not work with type definitions
'import/named': 'off',
"import/named": "off",
'prefer-promise-reject-errors': 'off',
"prefer-promise-reject-errors": "off",
quotes: ['warn', 'single', { avoidEscape: true }],
quotes: ["warn", "single", { avoidEscape: true }],
// this rule, if on, would require explicit return type on the `render` function
'@typescript-eslint/explicit-function-return-type': 'off',
"@typescript-eslint/explicit-function-return-type": "off",
// in plain CommonJS modules, you can't use `import foo = require('foo')` to pass this rule, so it has to be disabled
'@typescript-eslint/no-var-requires': 'off',
"@typescript-eslint/no-var-requires": "off",
// The core 'no-unused-vars' rules (in the eslint:recommended ruleset)
// does not work with type definitions
'no-unused-vars': 'off',
"no-unused-vars": "off",
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
// CUSTOM RULES
'object-shorthand': 'off',
'quote-props': 'off',
'camelcase': 'off'
}
}
"object-shorthand": "off",
"quote-props": "off",
camelcase: "off",
},
};

3
.npmrc
View file

@ -1,3 +0,0 @@
# pnpm-related options
shamefully-hoist=true
strict-peer-dependencies=false

3
.nycrc
View file

@ -1,3 +0,0 @@
{
"extends": "@quasar/quasar-app-extension-testing-e2e-cypress/nyc-config-preset"
}

View file

@ -32,23 +32,22 @@ quasar build
### 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
```
test:unit:ui
```
#### Unit test - without any UI, fully in a terminal
#### Unit test - Without any UI, fully in a terminal
```
test:unit:ci
```
#### 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)
#### Component test - via Playwright
```
test:component:frontend
test:component
```
#### E2E 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)
#### E2E test - via Playwright
```
test:e2e:frontend
test:e2e
```
### Customize the configuration

View file

@ -1,29 +0,0 @@
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.test.{js,jsx,ts,tsx}',
indexHtmlFile: 'test/cypress/support/component-index.html',
devServer: injectQuasarDevServerConfig()
}
})

View file

@ -10,13 +10,9 @@
"dev:electron": "quasar dev -m electron",
"build": "quasar build -m electron --publish never",
"test:unit:ui": "vitest --ui",
"test:unit": "vitest",
"test:unit:ci": "vitest run",
"test:e2e:frontend": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress open --e2e\"",
"test:e2e:frontend:ci": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress run --e2e\"",
"test:e2e:backend": "node \"node_modules/@playwright/test/cli.js\" test test/playwright/",
"test:component:frontend": "cross-env NODE_ENV=test cypress open --component",
"test:component:frontend:ci": "cross-env NODE_ENV=test cypress run --component"
"test:component": "node \"node_modules/@playwright/test/cli.js\" test test/playwright-e2e/",
"test:e2e": "node \"node_modules/@playwright/test/cli.js\" test test/playwright-e2e/"
},
"dependencies": {
"@electron/remote": "^2.0.10",
@ -33,7 +29,6 @@
"@playwright/test": "^1.37.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",
@ -42,12 +37,10 @@
"@vue/test-utils": "^2.0.0",
"app-root-path": "^3.1.0",
"autoprefixer": "^10.4.2",
"cypress": "^12.2.0",
"electron": "^25.5.0",
"electron-builder": "^24.3.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",

View file

@ -1,8 +1,7 @@
{
"@quasar/testing": {
"harnesses": [
"unit-vitest@alpha",
"e2e-cypress"
"unit-vitest@alpha"
]
},
"@quasar/testing-unit-vitest": {
@ -11,12 +10,5 @@
"typescript",
"ui"
]
},
"@quasar/testing-e2e-cypress": {
"options": [
"scripts",
"typescript",
"code-coverage"
]
}
}
}

View file

@ -1,11 +1,5 @@
{
"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"
}
}
}

View file

@ -1,11 +0,0 @@
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)')
})
})

View file

@ -1,2 +0,0 @@
videos/*
screenshots/*

View file

@ -1,46 +0,0 @@
// 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 {}

View file

@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View file

@ -1,30 +0,0 @@
// ***********************************************
// 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()

View file

@ -1,12 +0,0 @@
<!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>

View file

@ -1,51 +0,0 @@
// ***********************************************************
// 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 } })

View file

@ -1,17 +0,0 @@
// ***********************************************************
// 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'

View file

@ -1,26 +0,0 @@
<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>

View file

@ -1,20 +0,0 @@
<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>

View file

@ -0,0 +1,71 @@
const appRoot = require('app-root-path')
const { _electron: electron } = require('playwright')
const { test, expect } = require('@playwright/test')
const electronMainFilePath = appRoot + '/dist/electron/UnPackaged/electron-main.js'
const faFrontendRenderTimer = 1000
test('launch app', async () => {
const electronApp = await electron.launch({ args: [electronMainFilePath] })
// close app
await electronApp.close()
})
test('click resize button - smallify', async () => {
const electronApp = await electron.launch({ args: [electronMainFilePath] })
const appWindow = await electronApp.firstWindow()
await appWindow.waitForTimeout(faFrontendRenderTimer)
const resizeButton = await appWindow.$('.globalWindowButtons__resize')
await resizeButton.click()
const isMaximized = await appWindow.evaluate(() => window.faWindowControlAPI.checkWindowMaximized())
expect(isMaximized).toBe(false)
// close app
await electronApp.close()
})
test('click resize button - maximize', async () => {
const electronApp = await electron.launch({ args: [electronMainFilePath] })
const appWindow = await electronApp.firstWindow()
await appWindow.waitForTimeout(faFrontendRenderTimer)
const resizeButton = await appWindow.$('.globalWindowButtons__resize')
await resizeButton.click()
await resizeButton.click()
const isMaximized = await appWindow.evaluate(() => window.faWindowControlAPI.checkWindowMaximized())
expect(isMaximized).toBe(true)
// close app
await electronApp.close()
})
test('click minimize button', async () => {
const electronApp = await electron.launch({ args: [electronMainFilePath] })
const appWindow = await electronApp.firstWindow()
await appWindow.waitForTimeout(faFrontendRenderTimer)
const minimizeButton = await appWindow.$('.globalWindowButtons__minimize')
await minimizeButton.click()
const isMaximized = await appWindow.evaluate(() => window.faWindowControlAPI.checkWindowMaximized())
expect(isMaximized).toBe(false)
// close app
await electronApp.close()
})
test('click close button', async () => {
const electronApp = await electron.launch({ args: [electronMainFilePath] })
const appWindow = await electronApp.firstWindow()
await appWindow.waitForTimeout(faFrontendRenderTimer)
let windowIsClosed = false
appWindow.on('close', () => {
windowIsClosed = true
})
const closeButton = await appWindow.$('.globalWindowButtons__close')
await closeButton.click()
expect(windowIsClosed).toBe(true)
})

View file

@ -1,33 +0,0 @@
const appRoot = require('app-root-path')
const { _electron: electron } = require('playwright')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const { test } = require('@playwright/test')
test('launch app', async () => {
const electronApp = await electron.launch({ args: [appRoot + '/dist/electron/UnPackaged/electron-main.js'] })
// close app
await electronApp.close()
})
test('test resize', async () => {
const electronApp = await electron.launch({ args: [appRoot + '/dist/electron/UnPackaged/electron-main.js'] })
const appWindow = await electronApp.firstWindow()
await appWindow.waitForTimeout(3000)
const resizeButton = await appWindow.$('.globalWindowButtons__resize')
await resizeButton.click()
await appWindow.waitForTimeout(2000)
// close app
await electronApp.close()
})
/* test('save screenshot', async () => {
const electronApp = await electron.launch({ args: [appRoot + '/.quasar/electron/electron-main.js'] })
const window = await electronApp.firstWindow()
await window.waitForTimeout(15000)
await window.screenshot({ path: 'intro.png' })
// close app
await electronApp.close()
}) */

View file

@ -1 +0,0 @@
// This file will be run before each test file

View file

@ -7,12 +7,10 @@ import tsconfigPaths from 'vite-tsconfig-paths'
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}'
'src/**/*.vitest.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'
]
},
plugins: [

1500
yarn.lock

File diff suppressed because it is too large Load diff