feat(test): Add cypress for testing
This commit is contained in:
+3
-1
@@ -7,11 +7,13 @@ const baseRules = {
|
|||||||
allowModifiers: true,
|
allowModifiers: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'no-unused-expressions': ['off', { allowTernary: true }],
|
||||||
|
'chai-friendly/no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
|
||||||
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
|
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['@antfu', 'plugin:prettier/recommended'],
|
extends: ['@antfu', 'plugin:prettier/recommended'],
|
||||||
plugins: ['prettier'],
|
plugins: ['chai-friendly', 'prettier'],
|
||||||
rules: baseRules,
|
rules: baseRules,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
name: build-and-test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
node: [16]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout 🛎
|
||||||
|
uses: actions/checkout@master
|
||||||
|
|
||||||
|
- name: Setup node env 🏗
|
||||||
|
uses: actions/setup-node@v2.1.2
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
|
- name: Get yarn cache directory path 🛠
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
|
- name: Cache node_modules 📦
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
|
- name: Install dependencies 👨🏻💻
|
||||||
|
run: yarn
|
||||||
|
|
||||||
|
- name: Run linter 🧹
|
||||||
|
run: yarn lint
|
||||||
|
|
||||||
|
- name: Build Library 👷
|
||||||
|
run: yarn build
|
||||||
|
|
||||||
|
- name: Run tests 🧪
|
||||||
|
run: yarn test
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"testFiles": "cypress/**/*.spec.ts",
|
||||||
|
"componentFolder": ".",
|
||||||
|
"video": false,
|
||||||
|
"screenshotOnRunFailure": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { getElements } from '../../../examples/Stress/utils'
|
||||||
|
import { useStore } from '~/composables'
|
||||||
|
import { FlowStore } from '~/types'
|
||||||
|
import { isGraphEdge, isGraphNode } from '~/utils'
|
||||||
|
|
||||||
|
describe('test store action setElements', () => {
|
||||||
|
const setElements = async (store: FlowStore) => {
|
||||||
|
const elements = [
|
||||||
|
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
||||||
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
||||||
|
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
||||||
|
]
|
||||||
|
await store.setElements(elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
it('sets elements', async () => {
|
||||||
|
const store = useStore()
|
||||||
|
await setElements(store)
|
||||||
|
expect(store.elements).to.have.length(3)
|
||||||
|
store.$dispose()
|
||||||
|
})
|
||||||
|
|
||||||
|
context('elements pre-set', () => {
|
||||||
|
let store: FlowStore
|
||||||
|
beforeEach(async () => {
|
||||||
|
store = useStore()
|
||||||
|
await setElements(store)
|
||||||
|
})
|
||||||
|
afterEach(() => store.$dispose())
|
||||||
|
|
||||||
|
it('adds elements', () => {
|
||||||
|
store.addElements([{ id: '4', data: { label: 'Node 4' }, position: { x: 500, y: 500 } }])
|
||||||
|
expect(store.elements).to.have.length(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses elements to flow-elements', () => {
|
||||||
|
store.getEdges.forEach((edge) => expect(!isGraphNode(edge)).to.be.true)
|
||||||
|
store.getNodes.forEach((node) => expect(!isGraphEdge(node)).to.be.true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses elements to flow-elements (199 elements - stress test)', async () => {
|
||||||
|
await store.setElements(getElements())
|
||||||
|
store.getEdges.forEach((edge) => expect(!isGraphNode(edge)).to.be.true)
|
||||||
|
store.getNodes.forEach((node) => expect(!isGraphEdge(node)).to.be.true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has correct element ids', () => {
|
||||||
|
store.elements.forEach((el) => expect(['1', '2', 'e1-2']).to.include(el.id))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has correct element types', () => {
|
||||||
|
store.elements.forEach((el) => expect(['input', 'default']).to.include(el.type))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('nodes have correct label', () => {
|
||||||
|
store.getNodes.forEach((el) => {
|
||||||
|
if (el.id === '1') expect(el.data.label).to.eq('Node 1')
|
||||||
|
else expect(el.data.label).to.eq('Node 2')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('nodes have correct position', () => {
|
||||||
|
store.getNodes.forEach((el) => {
|
||||||
|
if (el.id === '1') expect(JSON.stringify(el.position)).to.eq(JSON.stringify({ x: 250, y: 5 }))
|
||||||
|
else expect(JSON.stringify(el.position)).to.eq(JSON.stringify({ x: 100, y: 100 }))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('edge has correct target and source', () => {
|
||||||
|
store.getEdges.forEach((el) => {
|
||||||
|
expect(el.source).to.eq('1')
|
||||||
|
expect(el.target).to.eq('2')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('edge has correct targetnode and sourceNode', () => {
|
||||||
|
store.getEdges.forEach((el) => {
|
||||||
|
expect(el.sourceNode.id).to.eq('1')
|
||||||
|
expect(el.targetNode.id).to.eq('2')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('edge is animated', () => {
|
||||||
|
store.getEdges.forEach((el) => expect(el.animated).to.eq(true))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { useStore } from '~/composables'
|
||||||
|
import { initialState } from '~/utils'
|
||||||
|
import { FlowState, FlowStore } from '~/types'
|
||||||
|
describe('test store state', () => {
|
||||||
|
let store: FlowStore
|
||||||
|
|
||||||
|
beforeEach(() => (store = useStore()))
|
||||||
|
afterEach(() => store.$dispose())
|
||||||
|
|
||||||
|
it('has any initial state', () => {
|
||||||
|
expect(store.$state).to.exist
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has default initial state', () => {
|
||||||
|
const initial = initialState()
|
||||||
|
Object.keys(store.$state).forEach((state) => {
|
||||||
|
const storedState = store[<keyof FlowState>state]
|
||||||
|
const initialVal = initial[<keyof FlowState>state]
|
||||||
|
expect(JSON.stringify(storedState)).to.eq(JSON.stringify(initialVal))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets state', () => {
|
||||||
|
store.setState({
|
||||||
|
zoomOnScroll: false,
|
||||||
|
})
|
||||||
|
expect(store.zoomOnScroll).to.eq(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('takes initial options', () => {
|
||||||
|
const store = useStore({
|
||||||
|
zoomOnScroll: false,
|
||||||
|
})
|
||||||
|
expect(store.zoomOnScroll).to.eq(false)
|
||||||
|
store.$dispose()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets custom node types', () => {
|
||||||
|
store.setState({
|
||||||
|
nodeTypes: ['custom'],
|
||||||
|
})
|
||||||
|
expect(Object.keys(store.getNodeTypes)).to.contain('custom')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets custom edge types', () => {
|
||||||
|
store.setState({
|
||||||
|
edgeTypes: ['custom'],
|
||||||
|
})
|
||||||
|
expect(Object.keys(store.getEdgeTypes)).to.contain('custom')
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import { startDevServer } from '@cypress/vite-dev-server'
|
||||||
|
|
||||||
|
export default ((on, config) => {
|
||||||
|
on('dev-server:start', async (options) =>
|
||||||
|
startDevServer({
|
||||||
|
options,
|
||||||
|
viteConfig: {
|
||||||
|
configFile: resolve(__dirname, '..', '..', 'vite.config.ts'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
return config
|
||||||
|
}) as Cypress.PluginConfig
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// ***********************************************
|
||||||
|
// 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 will overwrite an existing command --
|
||||||
|
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// ***********************************************************
|
||||||
|
// 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:
|
||||||
|
|
||||||
|
// Alternatively you can use CommonJS syntax:
|
||||||
|
// require('./commands')
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2018"
|
||||||
|
},
|
||||||
|
"include": ["**/*"]
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ const vfInstance = ref<FlowInstance>()
|
|||||||
const onElementsRemove = (elementsToRemove: Elements) => removeElements(elementsToRemove, elements.value)
|
const onElementsRemove = (elementsToRemove: Elements) => removeElements(elementsToRemove, elements.value)
|
||||||
const onConnect = (params: Edge | Connection) => addEdge(params, elements.value)
|
const onConnect = (params: Edge | Connection) => addEdge(params, elements.value)
|
||||||
const onLoad = (flowInstance: FlowInstance) => {
|
const onLoad = (flowInstance: FlowInstance) => {
|
||||||
flowInstance.fitView({ padding: 0.1 })
|
flowInstance.fitView({ padding: 0.1, nodes: ['2'] })
|
||||||
vfInstance.value = flowInstance
|
vfInstance.value = flowInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -26,7 +26,8 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly && tsc && rm -rf tmp",
|
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly && tsc && rm -rf tmp",
|
||||||
"prepublishOnly": "yarn build",
|
"prepublishOnly": "yarn build",
|
||||||
"test": "exit 0;",
|
"test": "cypress run-ct",
|
||||||
|
"test:open": "cypress open-ct",
|
||||||
"lint:js": "eslint --ext \".js,.jsx,.ts,.tsx\" --fix --ignore-path .gitignore .",
|
"lint:js": "eslint --ext \".js,.jsx,.ts,.tsx\" --fix --ignore-path .gitignore .",
|
||||||
"lint": "yarn lint:js"
|
"lint": "yarn lint:js"
|
||||||
},
|
},
|
||||||
@@ -42,14 +43,19 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^0.9.0",
|
"@antfu/eslint-config": "^0.9.0",
|
||||||
|
"@cypress/vite-dev-server": "^2.2.1",
|
||||||
|
"@cypress/vue": "^3.0.5",
|
||||||
"@rollup/plugin-replace": "^2.4.2",
|
"@rollup/plugin-replace": "^2.4.2",
|
||||||
"@types/dagre": "^0.7.46",
|
"@types/dagre": "^0.7.46",
|
||||||
"@vitejs/plugin-vue": "^1.9.3",
|
"@vitejs/plugin-vue": "^1.9.3",
|
||||||
"@vue/compiler-sfc": "^3.2.22",
|
"@vue/compiler-sfc": "^3.2.22",
|
||||||
"autoprefixer": "^10.3.7",
|
"autoprefixer": "^10.3.7",
|
||||||
|
"cypress": "8.5.0",
|
||||||
"dagre": "^0.8.5",
|
"dagre": "^0.8.5",
|
||||||
|
"esbuild": "0.13.4",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-chai-friendly": "^0.7.2",
|
||||||
"eslint-plugin-prettier": "^3.4.1",
|
"eslint-plugin-prettier": "^3.4.1",
|
||||||
"np": "^7.5.0",
|
"np": "^7.5.0",
|
||||||
"postcss-nested": "^5.0.6",
|
"postcss-nested": "^5.0.6",
|
||||||
@@ -59,7 +65,7 @@
|
|||||||
"typescript": "^4.4.4",
|
"typescript": "^4.4.4",
|
||||||
"typescript-transform-paths": "^3.3.1",
|
"typescript-transform-paths": "^3.3.1",
|
||||||
"unplugin-auto-import": "^0.4.12",
|
"unplugin-auto-import": "^0.4.12",
|
||||||
"vite": "^2.6.14",
|
"vite": "2.5.x",
|
||||||
"vite-svg-loader": "^2.2.0",
|
"vite-svg-loader": "^2.2.0",
|
||||||
"vue": "^3.2.22",
|
"vue": "^3.2.22",
|
||||||
"vue-router": "^4.0.12",
|
"vue-router": "^4.0.12",
|
||||||
|
|||||||
+2
-1
@@ -56,7 +56,8 @@ export const isEdge = (element: Node | Edge | Connection): element is Edge =>
|
|||||||
export const isNode = (element: Node | Edge | Connection): element is Node => 'id' in element && !isEdge(element)
|
export const isNode = (element: Node | Edge | Connection): element is Node => 'id' in element && !isEdge(element)
|
||||||
|
|
||||||
export const isGraphNode = (element: any): element is GraphNode => isNode(element) && '__vf' in element
|
export const isGraphNode = (element: any): element is GraphNode => isNode(element) && '__vf' in element
|
||||||
export const isGraphEdge = (element: any): element is GraphEdge => isEdge(element) && 'sourceTargetNodes' in element
|
export const isGraphEdge = (element: any): element is GraphEdge =>
|
||||||
|
isEdge(element) && 'sourceNode' in element && 'targetNode' in element
|
||||||
|
|
||||||
const getConnectedElements = (node: GraphNode, elements: Elements, dir: 'source' | 'target') => {
|
const getConnectedElements = (node: GraphNode, elements: Elements, dir: 'source' | 'target') => {
|
||||||
if (!isNode(node)) return []
|
if (!isNode(node)) return []
|
||||||
|
|||||||
+3
-2
@@ -20,7 +20,8 @@
|
|||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"types": [
|
"types": [
|
||||||
"vite/client"
|
"vite/client",
|
||||||
|
"cypress"
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"~/*": [
|
"~/*": [
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
{ "transform": "typescript-transform-paths", "afterDeclarations": true }
|
{ "transform": "typescript-transform-paths", "afterDeclarations": true }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src", "cypress/**/*.spec.ts"],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"build",
|
"build",
|
||||||
|
|||||||
+1
-1
@@ -59,6 +59,6 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: ['vue', '@vueuse/core'],
|
include: ['vue', '@vueuse/core', '@braks/revue-draggable'],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user