Add tests and GitHub Actions

Replace the Travis tests, which used a separate repo and no longer
ran, with tests in this repo. Each case in test/cases is indented as
a whole and should be unchanged, with optional syntax, fold and tag
checks. The cases are from the previous tests, plus script-setup.vue
for <script setup> keywords and a sync check in oneline.vim.

test/run.sh runs the cases with Vim in a pseudo terminal, as syntax in
Ex mode isn't the same, and Neovim headless.
This commit is contained in:
leafOfTree committed 2026-10-19 14:20:24 +00:00
1 parent d87fbbdde4
commit de812d609b
27 files changed
+974 -19

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'script': ['javascript'],
\ 'template': ['html'],
\ 'style': ['css'],
\},
\}
+20
View File
@@ -0,0 +1,20 @@
<template>
<p>{{ greeting }}</p>
</template>
<script>
export default {
data: function () {
return {
greeting: 'Hello'
};
},
};
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
+15
View File
@@ -0,0 +1,15 @@
" Uses syntax plugins in test/deps
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'template': ['html', 'pug'],
\ 'script': ['javascript', 'typescript', 'coffee'],
\ 'style': ['css', 'scss', 'sass', 'less', 'stylus'],
\ 'docs': 'markdown',
\ 'i18n': ['json', 'yaml'],
\ 'route': 'json',
\},
\'attribute': 1,
\'keyword': 1,
\'foldexpr': 1,
\'full_syntax': ['scss', 'less'],
\}
+160
View File
@@ -0,0 +1,160 @@
<template lang="pug">
div.set
span(id="123")
</template>
<template>
<template></template>
<div></div>
</template>
<template>
<p>{{ greeting }}</p>
</template>
<script>
export default {
data: function () {
return {
greeting: 'Hello'
};
},
};
</script>
<script lang="ts">
import Vue from 'vue'
import { User } from '@/types'
export default Vue.extend({
name: 'Home' as string,
data() {
return {
user: {} as User
}
},
computed: {
fullName(): string { // Computed Property returns a string
return `${this.user.firstName} ${this.user.lastName}`
}
},
mounted(): void { // The mounted hook returns nothing
...
}
})
</script>
<script lang="coffee">
# Assignment:
number = 42
opposite = true
# Conditions:
number = -42 if opposite
# Functions:
square = (x) -> x * x
# Arrays:
list = [1, 2, 3, 4, 5]
`
function hello() {}
console.log
a = 123;
`
# Objects:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
# Splats:
race = (winner, runners...) ->
print winner, runners
# Existence:
alert "I knew it!" if elvis?
# Array comprehensions:
cubes = (math.cube num for num in list)
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
<style scoped lang="scss">
$a: 100px;
div {
background: white;
}
</style>
<style lang="sass">
$w: 128px;
.bg
$w: 128px;
background: white;
align-items: center;
canvas
width: 100vw;
</style>
<style lang="less">
@list: 10 12;
canvas {
background: white;
width: 100vh;
}
</style>
<style lang="stylus">
@import 'vendor'
body
font: 12px Helvetica, Arial, sans-serif
a.button
border-radius: 5px
</style>
<route>
{
"name": "home",
"meta": {
"requiresAuth": true
}
}
</route>
<docs>
# This is the documentation for component.
</docs>
<i18n lang="yaml">
en:
hello: "Hello World!"
ja:
hello: "こんにちは、世界!"
</i18n>
<i18n>
{
"en": {
"language": "Language",
"hello": "hello, world!"
},
"ja": {
"language": "言語",
"hello": "こんにちは、世界!"
}
}
</i18n>
+15
View File
@@ -0,0 +1,15 @@
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'script': ['javascript'],
\ 'template': ['html'],
\ 'style': ['css'],
\},
\'foldexpr': 1,
\}
function! Check()
let levels = [1, 2, 1, 1, 1, 2, 3, 4, 4, 3, 3, 1, 1, 1, 1, 1, 2, 2, 1, 1]
for lnum in range(1, len(levels))
call ExpectFoldlevel(lnum, levels[lnum - 1])
endfor
endfunction
+20
View File
@@ -0,0 +1,20 @@
<template>
<p>{{ greeting }}</p>
</template>
<script>
export default {
data: function () {
return {
greeting: 'Hello'
};
},
};
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
+9
View File
@@ -0,0 +1,9 @@
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'template': ['html'],
\ 'script': ['javascript'],
\ 'style': ['css', 'sass', 'scss'],
\ 'codepen-additional': ['javascript', 'json'],
\},
\'initial_indent': ['script', 'style', 'codepen-additional'],
\}
+27
View File
@@ -0,0 +1,27 @@
<template>
<p>{{ greeting }}</p>
</template>
<script>
export default {
data: function () {
return {
greeting: 'Hello'
};
},
};
</script>
<style scoped lang="scss">
p {
font-size: 2em;
text-align: center;
}
</style>
<codepen-additional>
const easings = {
linear: '',
easeInQuad: '',
}
</codepen-additional>
+16
View File
@@ -0,0 +1,16 @@
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'template': ['html'],
\ 'script': ['javascript', 'typescript'],
\},
\'keyword': 1,
\'initial_indent': ['script', 'style', 'codepen-additional'],
\}
function! Check()
call ExpectSyntax('name', 'vueObjectKey')
call ExpectSyntax('data', 'vueObjectFuncKey')
call ExpectSyntax('data', 'vueObjectFuncName')
call ExpectSyntax('computed', 'vueObjectKey')
call ExpectSyntax('mounted', 'vueObjectFuncName')
endfunction
+45
View File
@@ -0,0 +1,45 @@
<script>
export default {
name: 'Home' as string,
data: function () {
return {
greeting: 'Hello'
};
},
data() {
return {
greeting: 'Hello'
};
},
computed: {
fullName() { // Computed Property returns a string
return `${this.user.firstName} ${this.user.lastName}`
}
},
mounted() { // The mounted hook returns nothing
...
}
};
</script>
<script lang="ts">
import Vue from 'vue'
import { User } from '@/types'
export default Vue.extend({
name: 'Home' as string,
data() {
return {
user: {} as User
}
},
computed: {
fullName(): string { // Computed Property returns a string
return `${this.user.firstName} ${this.user.lastName}`
}
},
mounted(): void { // The mounted hook returns nothing
...
}
})
</script>
+22
View File
@@ -0,0 +1,22 @@
" One-line blocks like <style>p { color: red; }</style>
function! Check()
call ExpectSyntax('script', 'javascriptScriptBlock')
call ExpectSyntax('p {', 'cssStyleBlock')
call ExpectSyntax('color', 'cssTextProp')
call ExpectSyntax('p {', 'cssStyleBlock')
call ExpectSyntax('color', 'cssTextProp')
call ExpectSyntax('Hello', 'htmlTemplateBlock')
call CheckSync()
endfunction
" Syntax from a sync point, as after jumping to a line not displayed yet
function! CheckSync()
silent edit!
call cursor(line('$'), 1)
call search('Hello', 'cW')
if index(SyntaxList(), 'htmlTemplateBlock') == -1
call Fail('after syncing, Hello should be htmlTemplateBlock, but got '
\.string(SyntaxList()))
endif
endfunction
+12
View File
@@ -0,0 +1,12 @@
<script src="./tmp"> </script>
<style lang="css">
p {
color: red;
}
</style>
<style>p { color: red; }</style>
<template><div>Hello</div></template>
+10
View File
@@ -0,0 +1,10 @@
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'template': ['html'],
\ 'script': ['javascript'],
\ 'style': [],
\},
\'attribute': 1,
\'keyword': 1,
\'foldexpr': 1,
\}
+151
View File
@@ -0,0 +1,151 @@
<template>
<div class="editor-page">
<div class="container page">
<div class="row">
<div class="col-md-10 offset-md-1 col-xs-12">
<RwvListErrors :errors="errors" />
<form @submit.prevent="onPublish(article.slug)">
<fieldset :disabled="inProgress">
<fieldset class="form-group">
<input
type="text"
class="form-control form-control-lg"
v-model="article.title"
placeholder="Article Title"
/>
</fieldset>
<fieldset class="form-group">
<input
type="text"
class="form-control"
v-model="article.description"
placeholder="What's this article about?"
/>
</fieldset>
<fieldset class="form-group">
<textarea
class="form-control"
rows="8"
v-model="article.body"
placeholder="Write your article (in markdown)"
>
</textarea>
</fieldset>
<fieldset class="form-group">
<input
type="text"
class="form-control"
placeholder="Enter tags"
v-model="tagInput"
@keypress.enter.prevent="addTag(tagInput)"
/>
<div class="tag-list">
<span
class="tag-default tag-pill"
v-for="(tag, index) of article.tagList"
:key="tag + index"
>
<i class="ion-close-round" @click="removeTag(tag)"> </i>
{{ tag }}
</span>
</div>
</fieldset>
</fieldset>
<button
:disabled="inProgress"
class="btn btn-lg pull-xs-right btn-primary"
type="submit"
>
Publish Article
</button>
</form>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import store from "@/store";
import RwvListErrors from "@/components/ListErrors";
import {
ARTICLE_PUBLISH,
ARTICLE_EDIT,
FETCH_ARTICLE,
ARTICLE_EDIT_ADD_TAG,
ARTICLE_EDIT_REMOVE_TAG,
ARTICLE_RESET_STATE
} from "@/store/actions.type";
export default {
name: "RwvArticleEdit",
components: { RwvListErrors },
props: {
previousArticle: {
type: Object,
required: false
}
},
async beforeRouteUpdate(to, from, next) {
// Reset state if user goes from /editor/:id to /editor
// The component is not recreated so we use to hook to reset the state.
await store.dispatch(ARTICLE_RESET_STATE);
return next();
},
async beforeRouteEnter(to, from, next) {
// SO: https://github.com/vuejs/vue-router/issues/1034
// If we arrive directly to this url, we need to fetch the article
await store.dispatch(ARTICLE_RESET_STATE);
if (to.params.slug !== undefined) {
await store.dispatch(
FETCH_ARTICLE,
to.params.slug,
to.params.previousArticle
);
}
return next();
},
async beforeRouteLeave(to, from, next) {
await store.dispatch(ARTICLE_RESET_STATE);
next();
},
data() {
return {
tagInput: null,
inProgress: false,
errors: {}
};
},
computed: {
...mapGetters(["article"])
},
methods: {
onPublish(slug) {
let action = slug ? ARTICLE_EDIT : ARTICLE_PUBLISH;
this.inProgress = true;
this.$store
.dispatch(action)
.then(({ data }) => {
this.inProgress = false;
this.$router.push({
name: "article",
params: { slug: data.article.slug }
});
})
.catch(({ response }) => {
this.inProgress = false;
this.errors = response.data.errors;
});
},
removeTag(tag) {
this.$store.dispatch(ARTICLE_EDIT_REMOVE_TAG, tag);
},
addTag(tag) {
this.$store.dispatch(ARTICLE_EDIT_ADD_TAG, tag);
this.tagInput = null;
}
}
};
</script>
+18
View File
@@ -0,0 +1,18 @@
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'template': ['html'],
\ 'script': ['javascript', 'typescript'],
\},
\'keyword': 1,
\}
function! Check()
call ExpectSyntax('withDefaults', 'vue3Keyword')
call ExpectSyntax('defineProps<', 'vue3Keyword')
call ExpectSyntax('defineEmits<', 'vue3Keyword')
call ExpectSyntax('defineModel<', 'vue3Keyword')
call ExpectSyntax('useTemplateRef', 'vue3Keyword')
call ExpectSyntax('ref<number>', 'vue3Keyword')
call ExpectNoSyntax('value < 3', 'vue3Keyword')
call ExpectSyntax('defineExpose', 'vue3Keyword')
endfunction
+15
View File
@@ -0,0 +1,15 @@
<template>
<div ref="el">{{ props.foo }}</div>
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{ foo?: string }>(), {
foo: 'a',
})
const emit = defineEmits<{ (e: 'change', id: number): void }>()
const model = defineModel<string>()
const el = useTemplateRef('el')
const count = ref<number>(1)
const small = count.value < 3
defineExpose({ el })
</script>
+18
View File
@@ -0,0 +1,18 @@
" Nested <template> and <docs> in <template>
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'script': ['javascript'],
\ 'template': ['html'],
\ 'style': ['css'],
\ 'docs': 'markdown',
\},
\}
function! Check()
while search('/template', 'W')
call ExpectTag(line('.'), 'template')
endwhile
normal! gg
call ExpectNoSyntax('/docs', 'markdownDocsBlock')
call ExpectSyntax('/docs', 'markdownDocsBlock')
endfunction
+23
View File
@@ -0,0 +1,23 @@
<template>
<template>
<div></div>
</template>
<div></div>
<template>
<div></div>
</template>
<div></div>
<docs></docs>
<div></div>
</template>
<template>
<template></template>
<div></div>
</template>
<docs>
# Hello
</docs>
+15
View File
@@ -0,0 +1,15 @@
let g:vim_vue_plugin_config = {
\'syntax': {
\ 'template': ['html'],
\ 'script': ['javascript', 'typescript'],
\ 'style': ['css'],
\},
\'keyword': 1,
\}
function! Check()
call ExpectSyntax('v-bind', 'cssFunctionName')
call ExpectSyntax('slotted', 'cssPseudoClassId')
call ExpectSyntax('global', 'cssPseudoClassId')
call ExpectSyntax('deep', 'cssPseudoClassId')
endfunction
+8
View File
@@ -0,0 +1,8 @@
<style scoped>
button {
color: v-bind(color);
}
.a :slotted(.b) :global(.c) :deep(.d) {
/* ... */
}
</style>