remove jest and add cypress for frontend testing

This commit is contained in:
sadnub
2021-02-24 13:51:22 -05:00
parent 665aebe114
commit 03d3eda6fc
18 changed files with 1424 additions and 2891 deletions

10
cypress.json Executable file
View File

@@ -0,0 +1,10 @@
{
"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
}

View File

@@ -1,66 +0,0 @@
module.exports = {
globals: {
__DEV__: true
},
setupFilesAfterEnv: [
'<rootDir>/test/jest/jest.setup.js'
],
// noStackTrace: true,
// bail: true,
// cache: false,
// verbose: true,
// watch: true,
collectCoverage: false,
coverageDirectory: '<rootDir>/test/jest/coverage',
collectCoverageFrom: [
'<rootDir>/src/**/*.vue',
'<rootDir>/src/**/*.js',
'<rootDir>/src/**/*.ts',
'<rootDir>/src/**/*.jsx'
],
coverageThreshold: {
global: {
// branches: 50,
// functions: 50,
// lines: 50,
// statements: 50
}
},
testMatch: [
'<rootDir>/test/jest/__tests__/**/*.spec.js',
'<rootDir>/test/jest/__tests__/**/*.test.js',
'<rootDir>/src/**/__tests__/*_jest.spec.js'
],
moduleFileExtensions: [
'vue',
'js',
'jsx',
'json',
'ts',
'tsx'
],
moduleNameMapper: {
'^vue$': '<rootDir>/node_modules/vue/dist/vue.common.js',
'^test-utils$': '<rootDir>/node_modules/@vue/test-utils/dist/vue-test-utils.js',
'^quasar$': '<rootDir>/node_modules/quasar/dist/quasar.common.js',
'^~/(.*)$': '<rootDir>/$1',
'^@/(.*)$': '<rootDir>/src/$1',
'^src/(.*)$': '<rootDir>/src/$1',
'.*css$': '<rootDir>/test/jest/utils/stub.css',
},
transform: {
'.*\\.vue$': 'vue-jest',
'.*\\.js$': 'babel-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
// use these if NPM is being flaky
//'.*\\.vue$': '<rootDir>/node_modules/@quasar/quasar-app-extension-testing-unit-jest/node_modules/vue-jest',
//'.*\\.js$': '<rootDir>/node_modules/@quasar/quasar-app-extension-testing-unit-jest/node_modules/babel-jest'
},
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!quasar/lang)'
],
snapshotSerializers: [
'<rootDir>/node_modules/jest-serializer-vue'
]
}

3875
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,8 @@
"scripts": {
"serve": "quasar dev",
"build": "quasar build",
"test:unit": "quasar test --unit jest"
"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.9.17",
@@ -21,12 +22,13 @@
"@quasar/app": "^2.1.15",
"@quasar/cli": "^1.1.3",
"@quasar/quasar-app-extension-testing": "^1.0.3",
"@quasar/quasar-app-extension-testing-unit-jest": "^1.0.1",
"@quasar/quasar-app-extension-testing-e2e-cypress": "^3.0.1",
"core-js": "^3.8.1",
"flush-promises": "^1.0.2",
"fs-extra": "^9.0.1",
"prismjs": "^1.22.0",
"vue-prism-editor": "^1.2.2"
"vue-prism-editor": "^1.2.2",
"eslint-plugin-cypress": "^2.11.1"
},
"browserslist": [
"last 4 Chrome versions",

View File

@@ -1,11 +1,6 @@
{
"@quasar/testing": {
"harnesses": [
"unit-jest"
]
},
"@quasar/testing-unit-jest": {
"babel": "babelrc",
"@quasar/testing": {},
"@quasar/testing-e2e-cypress": {
"options": [
"scripts"
]

View File

@@ -1,5 +1,5 @@
{
"unit-jest": {
"runnerCommand": "jest"
"e2e-cypress": {
"runnerCommand": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
}
}

View File

@@ -0,0 +1,7 @@
module.exports = {
extends: [
// Add Cypress-specific lint rules, globals and Cypress plugin
// See https://github.com/cypress-io/eslint-plugin-cypress#rules
'plugin:cypress/recommended',
],
};

2
test/cypress/.gitignore vendored Normal file
View File

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

View 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"
}

View File

@@ -0,0 +1,42 @@
/// <reference path="cypress" />
/// <reference path="../support/index.d.ts" />
// 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');
// });
// });

19
test/cypress/plugins/index.js Executable file
View File

@@ -0,0 +1,19 @@
/* 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
module.exports = (/*on, config*/) => {
//
};

View File

@@ -0,0 +1,50 @@
// ***********************************************
// 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) => { ... })
Cypress.Commands.add('dataCy', (value) => {
return cy.get(`[data-cy=${value}]`);
});
Cypress.Commands.add('testRoute', (route) => {
cy.location().should((loc) => {
expect(loc.hash).to.contain(route);
});
});
// these two commands let you persist local storage between tests
const LOCAL_STORAGE_MEMORY = {};
Cypress.Commands.add('saveLocalStorage', () => {
Object.keys(localStorage).forEach((key) => {
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
});
});
Cypress.Commands.add('restoreLocalStorage', () => {
Object.keys(LOCAL_STORAGE_MEMORY).forEach((key) => {
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
});
});

27
test/cypress/support/index.d.ts vendored Normal file
View File

@@ -0,0 +1,27 @@
declare namespace Cypress {
interface Chainable {
/**
* Custom command to select DOM element by data-cy attribute.
* @example cy.dataCy('greeting')
*/
dataCy<E extends Node = HTMLElement>(value: string): Chainable<JQuery<E>>;
/**
* Custom command to test being on a given route.
* @example cy.testRoute('home')
*/
testRoute(value: string): void;
/**
* Persist current local storage data.
* @example cy.saveLocalStorage()
*/
saveLocalStorage(): void;
/**
* Restore saved data to local storage.
* @example cy.restoreLocalStorage()
*/
restoreLocalStorage(): void;
}
}

27
test/cypress/support/index.js Executable file
View File

@@ -0,0 +1,27 @@
// ***********************************************************
// 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';
const resizeObserverLoopError = 'ResizeObserver loop limit exceeded';
Cypress.on('uncaught:exception', (err) => {
if (err.message.includes(resizeObserverLoopError)) {
// returning false here prevents Cypress from
// failing the test
return false;
}
});

View File

@@ -1,52 +0,0 @@
// No console.log() / setTimeout
// console.log = jest.fn(() => { throw new Error('Do not use console.log() in production') })
jest.setTimeout(1000)
// jest speedup when errors are part of the game
// Error.stackTraceLimit = 0
global.Promise = require('promise')
/*
import chai from 'chai'
// Make sure chai and jasmine ".not" play nice together
// https://medium.com/@RubenOostinga/combining-chai-and-jest-matchers-d12d1ffd0303
// updated here: https://www.andrewsouthpaw.com/jest-chai/
const originalNot = Object.getOwnPropertyDescriptor(chai.Assertion.prototype, 'not').get
Object.defineProperty(chai.Assertion.prototype, 'not', {
get() {
Object.assign(this, this.assignedNot)
return originalNot.apply(this)
},
set(newNot) {
this.assignedNot = newNot
return newNot
}
})
// Combine both jest and chai matchers on expect
const originalExpect = global.expect
global.expect = (actual) => {
const originalMatchers = originalExpect(actual)
const chaiMatchers = chai.expect(actual)
// Add middleware to Chai matchers to increment Jest assertions made
const { assertionsMade } = originalExpect.getState()
Object.defineProperty(chaiMatchers, 'to', {
get() {
originalExpect.setState({ assertionsMade: assertionsMade + 1 })
return chai.expect(actual)
},
})
const combinedMatchers = Object.assign(chaiMatchers, originalMatchers)
return combinedMatchers
}
Object.keys(originalExpect).forEach(key => (global.expect[key] = originalExpect[key]))
*/
// do this to make sure we don't get multiple hits from both webpacks when running SSR
setTimeout(()=>{
// do nothing
}, 1)

View File

@@ -1,70 +0,0 @@
// this is mapped in jest.config.js to resolve @vue/test-utils
import { createLocalVue, shallowMount } from 'test-utils'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import Quasar, { Cookies } from 'quasar'
const mockSsrContext = () => {
return {
req: {
headers: {}
},
res: {
setHeader: () => undefined
}
}
}
// https://eddyerburgh.me/mock-vuex-in-vue-unit-tests
export const mountQuasar = (component, options = {}) => {
const localVue = createLocalVue()
const app = {}
localVue.use(Vuex)
localVue.use(VueRouter)
localVue.use(Quasar)
const store = new Vuex.Store({})
const router = new VueRouter()
if (options) {
const ssrContext = options.ssr ? mockSsrContext() : null
if (options.cookies) {
const cookieStorage = ssrContext ? Cookies.parseSSR(ssrContext) : Cookies
const cookies = options.cookies
Object.keys(cookies).forEach(key => {
cookieStorage.set(key, cookies[key])
})
}
if (options.plugins) {
options.plugins.forEach(plugin => {
plugin({ app, store, router, Vue: localVue, ssrContext })
})
}
}
// mock vue-i18n
const $t = () => {}
const $tc = () => {}
const $n = () => {}
const $d = () => {}
return shallowMount(component, {
localVue: localVue,
store,
router,
mocks: { $t, $tc, $n, $d },
// Injections for Components with a QPage root Element
provide: {
pageContainer: true,
layout: {
header: {},
right: {},
footer: {},
left: {}
}
}
})
}

View File

@@ -1,33 +0,0 @@
import * as All from "quasar";
import Vue from "vue";
const {
Quasar,
Dialog,
Loading,
LoadingBar,
Meta,
Notify,
ClosePopup } = All;
const components = Object.keys(All).reduce((object, key) => {
const val = All[key];
if (val && val.component && val.component.name != null) {
object[key] = val;
}
return object;
}, {});
Vue.use(Quasar, {
components,
plugins: [
Dialog,
Loading,
LoadingBar,
Meta,
Notify
],
directives: [
ClosePopup
]
});

View File

@@ -1,9 +0,0 @@
/* for mocking out css files in jest.config.js */
/*
moduleNameMapper: {
...
'.*css$': '<rootDir>/test/jest/utils/stub.css'
},
*/