Migrate Cypress (E2E) tests to Vite and TypeScript
This commit progresses the migration from Vue CLI to Vite (#230). TypeScript migration: - Convert JavaScript Cypress tests and configurations to TypeScript. - Introduce `tsconfig.json` for Cypress, following official recommendation. Test execution: - Use Cypress CLI to run the tests. - Rename Cypress commands to reflect official naming conventions. - Start Vue server prior to Cypress execution, using `start-server-and-test` package based on official documentation. - Remove dependency on Vue CLI plugin ((`@vue/cli-plugin-e2e-cypress`). Configuration standardization (based on Cypress docs): - Delete unused `plugins/` directory. - Move test (spec) files to to the root directory. - Add official ESLint plugin (`eslint-plugin-cypress`). Changes for importing `vite.config.ts` into `cypress.config.ts`: - Add TypeScript import assertations to files importing JSON files. - Use ESM friendly way instead of `__dirname` to solve `ReferenceError: __dirname is not defined in ES module scrope`. Other changes: - Simplify comments in placeholder files. - Create Cypress specific `.gitignore` for enhanced maintainability, clarity and scalability. - Remove redundant `vue.config.cjs`.
This commit is contained in:
2
.github/workflows/tests.e2e.yaml
vendored
2
.github/workflows/tests.e2e.yaml
vendored
@@ -23,4 +23,4 @@ jobs:
|
||||
run: npm ci
|
||||
-
|
||||
name: Run e2e tests
|
||||
run: npm run test:e2e -- --headless
|
||||
run: npm run test:cy:run
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,6 +5,3 @@ dist/
|
||||
!.vscode/extensions.json
|
||||
#Electron-builder output
|
||||
/dist_electron
|
||||
# Cypress
|
||||
/tests/e2e/screenshots
|
||||
/tests/e2e/videos
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { defineConfig } from 'cypress';
|
||||
import setupPlugins from './tests/e2e/plugins/index';
|
||||
import ViteConfig from './vite.config';
|
||||
|
||||
const CYPRESS_BASE_DIR = 'tests/e2e/';
|
||||
|
||||
export default defineConfig({
|
||||
fixturesFolder: 'tests/e2e/fixtures',
|
||||
screenshotsFolder: 'tests/e2e/screenshots',
|
||||
videosFolder: 'tests/e2e/videos',
|
||||
fixturesFolder: `${CYPRESS_BASE_DIR}/fixtures`,
|
||||
screenshotsFolder: `${CYPRESS_BASE_DIR}/screenshots`,
|
||||
videosFolder: `${CYPRESS_BASE_DIR}/videos`,
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return setupPlugins(on, config);
|
||||
},
|
||||
specPattern: 'tests/e2e/specs/**/*.cy.{js,jsx,ts,tsx}',
|
||||
supportFile: 'tests/e2e/support/index.js',
|
||||
baseUrl: `http://localhost:${ViteConfig.server.port}/`,
|
||||
specPattern: `${CYPRESS_BASE_DIR}/**/*.cy.{js,jsx,ts,tsx}`, // Default: cypress/e2e/**/*.cy.{js,jsx,ts,tsx}
|
||||
supportFile: `${CYPRESS_BASE_DIR}/support/e2e.ts`,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -18,9 +18,9 @@ You could run other types of tests as well, but they may take longer time and ov
|
||||
|
||||
- Run unit tests: `npm run test:unit`
|
||||
- Run integration tests: `npm run test:integration`
|
||||
- Run e2e (end-to-end) tests
|
||||
- Interactive mode with GUI: `npm run test:e2e`
|
||||
- Headless mode without GUI: `npm run test:e2e -- --headless`
|
||||
- Run end-to-end (e2e) tests:
|
||||
- `npm run test:cy:open`: Run tests interactively using the development server with hot-reloading.
|
||||
- `npm run test:cy:run`: Run tests on the production build in a headless mode.
|
||||
|
||||
📖 Read more about testing in [tests](./tests.md).
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ There are different types of tests executed:
|
||||
## E2E tests
|
||||
|
||||
- Examine the live web application's functionality and performance.
|
||||
- Configured with the Vue CLI plugin [`e2e-cypress`](https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-plugin-e2e-cypress#readme).
|
||||
- Uses Cypress to run the tests.
|
||||
|
||||
## Automated checks
|
||||
|
||||
@@ -75,11 +75,9 @@ These checks validate various qualities like runtime execution, building process
|
||||
- Functions that calls `it()` from [Vitest](https://vitest.dev/) should have `it` prefix.
|
||||
- [`Stubs/`](./../tests/unit/shared/Stubs): Maintains stubs for component isolation, equipped with basic functionalities and, when necessary, spying or mocking capabilities.
|
||||
- [`./tests/integration/`](./../tests/integration/): Contains integration test files.
|
||||
- [`./tests/e2e/`](./../tests/e2e/)
|
||||
- [`cypress.config.ts`](./../cypress.config.ts): Cypress configuration file.
|
||||
- [`./tests/e2e/`](./../tests/e2e/): Base Cypress folder.
|
||||
- [`/specs/`](./../tests/e2e/specs/): Test files named with `.spec.js` extension.
|
||||
- [`/plugins/index.js`](./../tests/e2e/plugins/index.js): Plugin file executed before loading project.
|
||||
- [`/support/index.js`](./../tests/e2e/support/index.js): Support file, runs before every single spec file.
|
||||
- *(Ignored)* `/videos`: Asset folder for videos taken during tests.
|
||||
- *(Ignored)* `/screenshots`: Asset folder for Screenshots taken during tests.
|
||||
- [`cypress.config.ts`](./../cypress.config.ts): Cypress (E2E tests) configuration file.
|
||||
- [`./tests/e2e/`](./../tests/e2e/): Base Cypress folder, includes tests with `.cy.ts` extension.
|
||||
- [`/support/e2e.ts`](./../tests/e2e/support/e2e.ts): Support file, runs before every single spec file.
|
||||
- [`/tsconfig.json`]: TypeScript configuration for file Cypress code, improves IDE support, recommended to have by official documentation.
|
||||
- *(git ignored)* `/videos`: Asset folder for videos taken during tests.
|
||||
- *(git ignored)* `/screenshots`: Asset folder for Screenshots taken during tests.
|
||||
|
||||
9935
package-lock.json
generated
9935
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,8 @@
|
||||
"test:unit": "vitest run --dir tests/unit",
|
||||
"test:integration": "vitest run --dir tests/integration",
|
||||
"test:e2e": "vue-cli-service test:e2e",
|
||||
"test:cy:run": "start-server-and-test \"vite build && vite preview --port 7070\" http://localhost:7070 \"cypress run --config baseUrl=http://localhost:7070\"",
|
||||
"test:cy:open": "start-server-and-test \"vite --port 7070 --mode production\" http://localhost:7070 \"cypress open --config baseUrl=http://localhost:7070\"",
|
||||
"lint": "npm run lint:md && npm run lint:md:consistency && npm run lint:md:relative-urls && npm run lint:eslint && npm run lint:yaml",
|
||||
"icons:build": "node scripts/logo-update.js",
|
||||
"electron:build": "vue-cli-service electron:build",
|
||||
@@ -53,7 +55,6 @@
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@vitejs/plugin-legacy": "^4.1.1",
|
||||
"@vitejs/plugin-vue2": "^2.2.0",
|
||||
"@vue/cli-plugin-e2e-cypress": "^5.0.8",
|
||||
"@vue/eslint-config-airbnb-with-typescript": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.3",
|
||||
"@vue/test-utils": "^1.3.6",
|
||||
@@ -67,6 +68,7 @@
|
||||
"electron-updater": "^6.1.4",
|
||||
"electron-vite": "^1.0.27",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-plugin-cypress": "^2.14.0",
|
||||
"eslint-plugin-vue": "^9.6.0",
|
||||
"eslint-plugin-vuejs-accessibility": "^1.2.0",
|
||||
"icon-gen": "^3.0.1",
|
||||
@@ -78,6 +80,7 @@
|
||||
"remark-preset-lint-consistent": "^5.1.2",
|
||||
"remark-validate-links": "^12.1.1",
|
||||
"sass": "^1.64.1",
|
||||
"start-server-and-test": "^2.0.0",
|
||||
"svgexport": "^0.4.2",
|
||||
"terser": "^5.19.2",
|
||||
"tslib": "~2.4.0",
|
||||
|
||||
@@ -5,6 +5,9 @@ module.exports = {
|
||||
env: {
|
||||
'cypress/globals': true,
|
||||
},
|
||||
extends: [
|
||||
'plugin:cypress/recommended',
|
||||
],
|
||||
rules: {
|
||||
strict: 'off',
|
||||
},
|
||||
|
||||
2
tests/e2e/.gitignore
vendored
Normal file
2
tests/e2e/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
screenshots
|
||||
videos
|
||||
@@ -1,21 +0,0 @@
|
||||
/* eslint-disable arrow-body-style */
|
||||
// https://docs.cypress.io/guides/tooling/plugins-guide
|
||||
|
||||
// if you need a custom webpack configuration you can uncomment the following import
|
||||
// and then use the `file:preprocessor` event
|
||||
// as explained in the cypress docs
|
||||
// https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples
|
||||
|
||||
// /* eslint-disable import/no-extraneous-dependencies, global-require */
|
||||
// const webpack = require('@cypress/webpack-preprocessor')
|
||||
|
||||
export default (on, config) => {
|
||||
// on('file:preprocessor', webpack({
|
||||
// webpackOptions: require('@vue/cli-service/webpack.config'),
|
||||
// watchOptions: {}
|
||||
// }))
|
||||
|
||||
return {
|
||||
...config,
|
||||
};
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
// ***********************************************
|
||||
// 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) => { ... })
|
||||
2
tests/e2e/support/commands.ts
Normal file
2
tests/e2e/support/commands.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// This file is a placeholder to include code to create various custom commands
|
||||
// and overwrite existing commands, see: https://docs.cypress.io/api/cypress-api/custom-commands
|
||||
6
tests/e2e/support/e2e.ts
Normal file
6
tests/e2e/support/e2e.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
This file is processed and loaded automatically before test files.
|
||||
It's designed to put global configuration and behavior that modifies Cypress.
|
||||
*/
|
||||
|
||||
import './commands';
|
||||
@@ -1,20 +0,0 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your 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.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
16
tests/e2e/tsconfig.json
Normal file
16
tests/e2e/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom"
|
||||
],
|
||||
"types": [
|
||||
"cypress",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ViteAppMetadata } from '@/infrastructure/Metadata/Vite/ViteAppMetadata';
|
||||
import packageJson from '@/../package.json';
|
||||
import packageJson from '@/../package.json' assert { type: 'json' };
|
||||
import { PropertyKeys } from '@tests/shared/TypeHelpers';
|
||||
|
||||
describe('ViteAppMetadata', () => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { resolve } from 'path';
|
||||
import { resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { VITE_ENVIRONMENT_KEYS } from './src/infrastructure/Metadata/Vite/ViteEnvironmentKeys';
|
||||
import tsconfigJson from './tsconfig.json';
|
||||
import packageJson from './package.json';
|
||||
import tsconfigJson from './tsconfig.json' assert { type: 'json' };
|
||||
import packageJson from './package.json' assert { type: 'json' };
|
||||
|
||||
export function getAliasesFromTsConfig(): Record<string, string> {
|
||||
const { paths } = tsconfigJson.compilerOptions;
|
||||
@@ -9,12 +10,18 @@ export function getAliasesFromTsConfig(): Record<string, string> {
|
||||
const pathFolder = paths[pathName][0];
|
||||
const aliasFolder = pathFolder.substring(0, pathFolder.length - 1); // trim * from end
|
||||
const aliasName = pathName.substring(0, pathName.length - 2); // trim /* from end
|
||||
const aliasPath = resolve(__dirname, aliasFolder);
|
||||
const aliasPath = resolve(getSelfDirectoryAbsolutePath(), aliasFolder);
|
||||
aliases[aliasName] = aliasPath;
|
||||
return aliases;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function getSelfDirectoryAbsolutePath() {
|
||||
const filePath = fileURLToPath(import.meta.url);
|
||||
const directoryPath = dirname(filePath);
|
||||
return directoryPath;
|
||||
}
|
||||
|
||||
type ViteEnvironmentKeyValues = {
|
||||
[K in typeof VITE_ENVIRONMENT_KEYS[keyof typeof VITE_ENVIRONMENT_KEYS]]: string
|
||||
};
|
||||
|
||||
@@ -4,10 +4,10 @@ import { defineConfig } from 'vite';
|
||||
import legacy from '@vitejs/plugin-legacy';
|
||||
import vue from '@vitejs/plugin-vue2';
|
||||
import ViteYaml from '@modyfi/vite-plugin-yaml';
|
||||
import { getAliasesFromTsConfig, getClientEnvironmentVariables } from './vite-config-helper';
|
||||
import { getAliasesFromTsConfig, getClientEnvironmentVariables, getSelfDirectoryAbsolutePath } from './vite-config-helper';
|
||||
|
||||
const WEB_DIRECTORY = resolve(__dirname, 'src/presentation');
|
||||
const TEST_INITIALIZATION_FILE = resolve(__dirname, 'tests/shared/bootstrap/setup.ts');
|
||||
const WEB_DIRECTORY = resolve(getSelfDirectoryAbsolutePath(), 'src/presentation');
|
||||
const TEST_INITIALIZATION_FILE = resolve(getSelfDirectoryAbsolutePath(), 'tests/shared/bootstrap/setup.ts');
|
||||
const NODE_CORE_MODULES = ['os', 'child_process', 'fs', 'path'];
|
||||
|
||||
export default defineConfig({
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/* Keeping here until Vitest migration */
|
||||
|
||||
const { resolve } = require('path');
|
||||
const { defineConfig } = require('@vue/cli-service');
|
||||
const packageJson = require('./package.json');
|
||||
const tsconfigJson = require('./tsconfig.json');
|
||||
|
||||
loadVueAppRuntimeVariables();
|
||||
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true,
|
||||
chainWebpack: (config) => {
|
||||
changeAppEntryPoint('./src/presentation/main.ts', config);
|
||||
addWebpackRule('yaml', /\.ya?ml$/, 'js-yaml-loader', config);
|
||||
},
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
...getAliasesFromTsConfig(),
|
||||
},
|
||||
fallback: {
|
||||
/* Tell webpack to ignore polyfilling them because Node core modules are never used
|
||||
for browser code but only for desktop where Electron supports them. */
|
||||
...ignorePolyfills('os', 'child_process', 'fs', 'path'),
|
||||
},
|
||||
},
|
||||
// Fix compilation failing on macOS when running unit/integration tests
|
||||
externals: ['fsevents'],
|
||||
// Use something other than default source mapper or babel cannot
|
||||
// log stacks like `console.log(new Error().stack)`
|
||||
devtool: 'eval-source-map',
|
||||
},
|
||||
});
|
||||
|
||||
function addWebpackRule(name, test, loader, config) {
|
||||
config.module
|
||||
.rule(name)
|
||||
.test(test)
|
||||
.use(loader)
|
||||
.loader(loader)
|
||||
.end();
|
||||
}
|
||||
|
||||
function changeAppEntryPoint(entryPath, config) {
|
||||
config.entry('app').clear().add(entryPath).end();
|
||||
}
|
||||
|
||||
function loadVueAppRuntimeVariables() {
|
||||
process.env.VUE_APP_VERSION = packageJson.version;
|
||||
process.env.VUE_APP_NAME = packageJson.name;
|
||||
process.env.VUE_APP_REPOSITORY_URL = packageJson.repository.url;
|
||||
process.env.VUE_APP_HOMEPAGE_URL = packageJson.homepage;
|
||||
process.env.VUE_APP_SLOGAN = packageJson.slogan;
|
||||
}
|
||||
|
||||
function ignorePolyfills(...moduleNames) {
|
||||
return moduleNames.reduce((obj, module) => {
|
||||
obj[module] = false;
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function getAliasesFromTsConfig() {
|
||||
const { paths } = tsconfigJson.compilerOptions;
|
||||
return Object.keys(paths).reduce((aliases, pathName) => {
|
||||
const pathFolder = paths[pathName][0];
|
||||
const aliasFolder = pathFolder.substring(0, pathFolder.length - 1); // trim * from end
|
||||
const aliasName = pathName.substring(0, pathName.length - 2); // trim /* from end
|
||||
const aliasPath = resolve(__dirname, aliasFolder);
|
||||
aliases[aliasName] = aliasPath;
|
||||
return aliases;
|
||||
}, {});
|
||||
}
|
||||
Reference in New Issue
Block a user