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 // 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 // `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted
parserOptions: { parserOptions: {
parser: require.resolve('@typescript-eslint/parser'), parser: require.resolve("@typescript-eslint/parser"),
extraFileExtensions: ['.vue'] extraFileExtensions: [".vue"],
}, },
env: { env: {
browser: true, browser: true,
es2021: true, es2021: true,
node: true, node: true,
'vue/setup-compiler-macros': true "vue/setup-compiler-macros": true,
}, },
// Rules order is important, please avoid shuffling them // 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 // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
// ESLint typescript rules // ESLint typescript rules
'plugin:@typescript-eslint/recommended', "plugin:@typescript-eslint/recommended",
// Uncomment any of the lines below to choose desired strictness, // Uncomment any of the lines below to choose desired strictness,
// but leave only one uncommented! // but leave only one uncommented!
// See https://eslint.vuejs.org/rules/#available-rules // See https://eslint.vuejs.org/rules/#available-rules
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention) "plugin:vue/vue3-essential", // Priority A: Essential (Error Prevention)
'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) "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-recommended", // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
'standard',
'plugin:cypress/recommended'
"standard",
], ],
plugins: [ plugins: [
// required to apply rules which need type information // 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 // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
// required to lint *.vue files // required to lint *.vue files
'vue' "vue",
], ],
globals: { globals: {
ga: 'readonly', // Google Analytics ga: "readonly", // Google Analytics
cordova: 'readonly', cordova: "readonly",
__statics: 'readonly', __statics: "readonly",
__QUASAR_SSR__: 'readonly', __QUASAR_SSR__: "readonly",
__QUASAR_SSR_SERVER__: 'readonly', __QUASAR_SSR_SERVER__: "readonly",
__QUASAR_SSR_CLIENT__: 'readonly', __QUASAR_SSR_CLIENT__: "readonly",
__QUASAR_SSR_PWA__: 'readonly', __QUASAR_SSR_PWA__: "readonly",
process: 'readonly', process: "readonly",
Capacitor: 'readonly', Capacitor: "readonly",
chrome: 'readonly' chrome: "readonly",
}, },
// add your custom rules here // add your custom rules here
rules: { rules: {
// allow async-await // allow async-await
'generator-star-spacing': 'off', "generator-star-spacing": "off",
// allow paren-less arrow functions // allow paren-less arrow functions
'arrow-parens': 'off', "arrow-parens": "off",
'one-var': 'off', "one-var": "off",
'no-void': 'off', "no-void": "off",
'multiline-ternary': 'off', "multiline-ternary": "off",
'import/first': 'off', "import/first": "off",
'import/namespace': 'error', "import/namespace": "error",
'import/default': 'error', "import/default": "error",
'import/export': 'error', "import/export": "error",
'import/extensions': 'off', "import/extensions": "off",
'import/no-unresolved': 'off', "import/no-unresolved": "off",
'import/no-extraneous-dependencies': 'off', "import/no-extraneous-dependencies": "off",
// The core 'import/named' rules // The core 'import/named' rules
// does not work with type definitions // 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 // 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 // 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) // The core 'no-unused-vars' rules (in the eslint:recommended ruleset)
// does not work with type definitions // does not work with type definitions
'no-unused-vars': 'off', "no-unused-vars": "off",
// allow debugger during development only // 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 // CUSTOM RULES
'object-shorthand': 'off', "object-shorthand": "off",
'quote-props': 'off', "quote-props": "off",
'camelcase': '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: ### 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
``` ```
test:unit: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 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 ### 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", "dev:electron": "quasar dev -m electron",
"build": "quasar build -m electron --publish never", "build": "quasar build -m electron --publish never",
"test:unit:ui": "vitest --ui", "test:unit:ui": "vitest --ui",
"test:unit": "vitest",
"test:unit:ci": "vitest run", "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:component": "node \"node_modules/@playwright/test/cli.js\" test test/playwright-e2e/",
"test:e2e:frontend:ci": "cross-env NODE_ENV=test start-test \"quasar dev\" http-get://localhost:9000 \"cypress run --e2e\"", "test:e2e": "node \"node_modules/@playwright/test/cli.js\" test test/playwright-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"
}, },
"dependencies": { "dependencies": {
"@electron/remote": "^2.0.10", "@electron/remote": "^2.0.10",
@ -33,7 +29,6 @@
"@playwright/test": "^1.37.1", "@playwright/test": "^1.37.1",
"@quasar/app-vite": "^1.3.0", "@quasar/app-vite": "^1.3.0",
"@quasar/quasar-app-extension-testing": "^2.1.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", "@quasar/quasar-app-extension-testing-unit-vitest": "^0.1.0",
"@types/node": "^12.20.21", "@types/node": "^12.20.21",
"@typescript-eslint/eslint-plugin": "^5.10.0", "@typescript-eslint/eslint-plugin": "^5.10.0",
@ -42,12 +37,10 @@
"@vue/test-utils": "^2.0.0", "@vue/test-utils": "^2.0.0",
"app-root-path": "^3.1.0", "app-root-path": "^3.1.0",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
"cypress": "^12.2.0",
"electron": "^25.5.0", "electron": "^25.5.0",
"electron-builder": "^24.3.0", "electron-builder": "^24.3.0",
"eslint": "^8.10.0", "eslint": "^8.10.0",
"eslint-config-standard": "^17.0.0", "eslint-config-standard": "^17.0.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.19.1", "eslint-plugin-import": "^2.19.1",
"eslint-plugin-n": "^15.0.0", "eslint-plugin-n": "^15.0.0",
"eslint-plugin-promise": "^6.0.0", "eslint-plugin-promise": "^6.0.0",

View file

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

View file

@ -1,11 +1,5 @@
{ {
"unit-vitest": { "unit-vitest": {
"runnerCommand": "vitest run" "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({ export default defineConfig({
test: { test: {
environment: 'jsdom', environment: 'jsdom',
setupFiles: 'test/vitest/setup-file.ts',
include: [ include: [
// Matches vitest tests in any subfolder of 'src' or into 'test/vitest/__tests__' // Matches vitest tests in any subfolder of 'src' or into 'test/vitest/__tests__'
// Matches all files with extension 'js', 'jsx', 'ts' and 'tsx' // Matches all files with extension 'js', 'jsx', 'ts' and 'tsx'
'src/**/*.vitest.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,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: [ plugins: [

1500
yarn.lock

File diff suppressed because it is too large Load diff