Skip to content

Commit

Permalink
Create initial project structure
Browse files Browse the repository at this point in the history
Project configured with:
- Vue 3
- Vuetify
- Typescript
- Vue Router
- Pinia
- Vitest
- Cypress
- Eslint
- Prettier

Unit tests (with vitest) should only be done over free functions, composables and stores.

Components and behavior in general should receive integrations tests (with cypress).
  • Loading branch information
rafaellehmkuhl authored and patrickelectric committed May 18, 2022
1 parent 2623006 commit e524e2f
Show file tree
Hide file tree
Showing 31 changed files with 3,862 additions and 0 deletions.
79 changes: 79 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier',
],
env: {
'browser': true,
'es2022': true,
'vue/setup-compiler-macros': true,
},
plugins: ['simple-import-sort'],
rules: {
'func-style': ['error', 'declaration'],
'import/extensions': 'off',
'import/order': 'off',
'max-len': ['error', { code: 120 }],
'no-alert': 'off',
'no-console': 'off',
'no-continue': 'off',
'no-extra-parens': ['error', 'all'],
// modified https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/rules/style.js#L339
// In our opinion, readability comes first and ForOF statements are more readable,
// so we remove the ForOfStatement block.
'no-restricted-syntax': [
'error',
{
selector: 'ForInStatement',
message:
'for..in loops iterate over the entire prototype chain, which is virtually never what you want.' +
'Use Object.{keys,values,entries}, and iterate over the resulting array.',
},
{
selector: 'LabeledStatement',
message:
'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
},
{
selector: 'WithStatement',
message:
'`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
},
],
'no-param-reassign': 'off',
'no-shadow': 'off',
'no-useless-constructor': 'off',
'semi': ['error', 'never'],
'simple-import-sort/exports': 'error',
'simple-import-sort/imports': 'error',
'sort-imports': 'off',
'@typescript-eslint/no-useless-constructor': ['error'],
'@typescript-eslint/no-shadow': ['error'],
'@typescript-eslint/explicit-function-return-type': [
'error',
{ allowExpressions: true },
],
'vue/no-unused-properties': [
'error',
{
groups: ['props', 'data', 'computed', 'methods', 'setup'],
deepData: true,
ignorePublicMembers: false,
},
],
'vue/valid-v-slot': ['error', { allowModifiers: true }],
'no-await-in-loop': 'off',
},
overrides: [
{
files: ['cypress/integration/**.spec.{js,ts,jsx,tsx}'],
extends: ['plugin:cypress/recommended'],
},
],
}
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

/cypress/videos/
/cypress/screenshots/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
3 changes: 3 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"baseUrl": "http://localhost:5050"
}
9 changes: 9 additions & 0 deletions cypress/integration/rootClicker.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
describe('Test root clicker', () => {
it('visits the app root url and test button functionality', () => {
cy.visit('/')
cy.contains('h1', 'This is the home page')
cy.get('button').click()
cy.contains('p', 'Clicked the button 1 times.')
cy.contains('p', 'Double this value is 2.')
})
})
5 changes: 5 additions & 0 deletions cypress/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default ((on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
return config
}) as Cypress.PluginConfig
9 changes: 9 additions & 0 deletions cypress/plugins/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["./**/*"],
"compilerOptions": {
"module": "CommonJS",
"preserveValueImports": false,
"types": ["node", "cypress/types/cypress"]
}
}
Empty file added cypress/support/commands.ts
Empty file.
1 change: 1 addition & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './commands'
10 changes: 10 additions & 0 deletions cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["./integration/**/*", "./support/**/*"],
"compilerOptions": {
"isolatedModules": false,
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
}
}
1 change: 1 addition & 0 deletions env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
16 changes: 16 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cockpit</title>
</head>

<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>

</html>
60 changes: 60 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "cockpit",
"version": "0.0.0",
"scripts": {
"serve": "vite preview",
"build": "vite build",
"test:unit": "vitest",
"coverage": "vitest --coverage",
"test:e2e": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress open'",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"dev": "vite",
"preview": "vite preview --port 5050",
"test:e2e:ci": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress run'",
"typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false"
},
"dependencies": {
"@mdi/font": "5.9.55",
"pinia": "^2.0.13",
"roboto-fontface": "*",
"vue": "^3.2.33",
"vue-router": "^4.0.14",
"vuetify": "^3.0.0-beta.0",
"webfontloader": "^1.0.0"
},
"devDependencies": {
"@pinia/testing": "^0.0.12",
"@rushstack/eslint-patch": "^1.1.0",
"@types/jsdom": "^16.2.14",
"@types/node": "^16.11.27",
"@types/webfontloader": "^1.0.0",
"@vitejs/plugin-vue": "^2.3.1",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"@vue/test-utils": "^2.0.0-rc.20",
"@vue/tsconfig": "^0.1.3",
"@vuetify/vite-plugin": "^1.0.0-alpha.0",
"c8": "^7.11.3",
"cypress": "^9.5.4",
"eslint": "^8.5.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-vue": "^8.2.0",
"jsdom": "^19.0.0",
"prettier": "^2.5.1",
"sass": "^1.38.0",
"sass-loader": "^10.0.0",
"start-server-and-test": "^1.14.0",
"typescript": "~4.6.3",
"vite": "^2.9.5",
"vitest": "^0.9.3",
"vue-cli-plugin-vuetify": "~2.4.8",
"vue-tsc": "^0.34.7",
"vuetify-loader": "^2.0.0-alpha.0"
},
"prettier": {
"semi": false,
"singleQuote": true,
"quoteProps": "consistent"
}
}
Binary file added public/favicon.ico
Binary file not shown.
21 changes: 21 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<v-app>
<v-main>
<router-view />
</v-main>
</v-app>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App',
data() {
return {
//
}
},
})
</script>
20 changes: 20 additions & 0 deletions src/components/CounterCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<div>
<v-card>
<v-card-title>Counter card</v-card-title>
<v-card-text>
<p>Clicked the button {{ store.counter }} times.</p>
<p>Double this value is {{ store.doubleCount }}.</p>
</v-card-text>
<v-card-actions>
<v-btn @click="store.increment">Click me!</v-btn>
</v-card-actions>
</v-card>
</div>
</template>

<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
</script>
11 changes: 11 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createPinia } from 'pinia'
import { createApp } from 'vue'

import App from './App.vue'
import vuetify from './plugins/vuetify'
import { loadFonts } from './plugins/webfontloader'
import router from './router'

loadFonts()

createApp(App).use(router).use(vuetify).use(createPinia()).mount('#app')
7 changes: 7 additions & 0 deletions src/plugins/vuetify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Styles
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'

import { createVuetify } from 'vuetify'

export default createVuetify()
11 changes: 11 additions & 0 deletions src/plugins/webfontloader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export async function loadFonts(): Promise<void> {
const webFontLoader = await import(
/* webpackChunkName: "webfontloader" */ 'webfontloader'
)

webFontLoader.load({
google: {
families: ['Roboto:100,300,400,500,700,900&display=swap'],
},
})
}
24 changes: 24 additions & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createRouter, createWebHistory } from 'vue-router'

import HomeView from '../views/HomeView.vue'

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
],
})

export default router
3 changes: 3 additions & 0 deletions src/shims-vuetify.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module 'vuetify'
declare module 'vuetify/lib/components'
declare module 'vuetify/lib/directives'
16 changes: 16 additions & 0 deletions src/stores/counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineStore } from 'pinia'

export const useCounterStore = defineStore({
id: 'counter',
state: () => ({
counter: 0,
}),
getters: {
doubleCount: (state) => state.counter * 2,
},
actions: {
increment() {
this.counter++
},
},
})
2 changes: 2 additions & 0 deletions src/styles/_variables.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Place SASS variable overrides here
// $font-size-root: 18px;
5 changes: 5 additions & 0 deletions src/tests/basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { expect, test } from 'vitest'

test('Math.sqrt()', () => {
expect(Math.sqrt(4)).toBe(2)
})
16 changes: 16 additions & 0 deletions src/views/AboutView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>
Loading

0 comments on commit e524e2f

Please sign in to comment.