Skip to content
This repository has been archived by the owner on Oct 6, 2023. It is now read-only.

Commit

Permalink
feat(ui): test output tab, closes #51
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Dec 20, 2021
1 parent 58df8b7 commit d787ef5
Show file tree
Hide file tree
Showing 17 changed files with 493 additions and 6 deletions.
1 change: 1 addition & 0 deletions examples/demo/src/vue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('vue', () => {
},
template: '<div>hello</div>',
})
console.log(app)
expect(typeof app.version).toBe('string')
const el = document.createElement('div')
document.body.appendChild(el)
Expand Down
8 changes: 7 additions & 1 deletion packages/peeky-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@
"monaco-editor": "^0.26.1",
"v-tooltip": "^4.0.0-alpha.1",
"vue": "^3.2.20",
"vue-router": "^4.0.3"
"vue-router": "^4.0.3",
"vue-resize": "^2.0.0-alpha.1",
"xterm": "^4.15.0",
"xterm-addon-fit": "^0.5.0",
"xterm-addon-search": "^0.8.1",
"xterm-addon-web-links": "^0.4.0",
"xterm-addon-webgl": "^0.11.3"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.10.2",
Expand Down
Binary file not shown.
34 changes: 34 additions & 0 deletions packages/peeky-client/src/features/BaseTab.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
inheritAttrs: false,
})
</script>

<template>
<router-link
v-slot="{ isExactActive, href, navigate }"
v-bind="$attrs"
custom
>
<a
v-bind="$attrs"
:href="href"
class="px-4 h-10 inline-flex items-center hover:bg-primary-50 dark:hover:bg-primary-900 relative"
:class="{
'text-primary-500 dark:text-primary-400': isExactActive,
}"
@click="navigate"
>
<slot />

<transition name="scale-x">
<div
v-if="isExactActive"
class="absolute bottom-0 left-0 w-full h-[2px] bg-primary-500 dark:bg-primary-400"
/>
</transition>
</a>
</router-link>
</template>
200 changes: 200 additions & 0 deletions packages/peeky-client/src/features/terminal/Terminal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<script lang="ts">
import 'xterm/css/xterm.css'
import { ref, onMounted, watch, onUnmounted, onActivated, computed, defineComponent, PropType } from 'vue'
import { IDisposable, Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import { SearchAddon } from 'xterm-addon-search'
import { WebLinksAddon } from 'xterm-addon-web-links'
import { WebglAddon } from 'xterm-addon-webgl'
// @ts-ignore
import { ResizeObserver } from 'vue-resize'
import { useSettings } from '../settings'
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(navigator.platform)
const isWebgl2Supported = (() => {
let isSupported = window.WebGL2RenderingContext ? undefined : false
return () => {
if (isSupported === undefined) {
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl2', { depth: false, antialias: false })
isSupported = gl instanceof window.WebGL2RenderingContext
}
return isSupported
}
})()
const defaultTheme = {
foreground: '#1e1e25',
background: '#fff',
cursor: '#ccc',
cursorAccent: '#ddd',
selection: '#00000040',
black: '#000000',
red: '#e83030',
brightRed: '#e83030',
green: '#42b983',
brightGreen: '#42b983',
brightYellow: '#ea6e00',
yellow: '#ea6e00',
magenta: '#e83030',
brightMagenta: '#e83030',
cyan: '#03c2e6',
brightBlue: '#03c2e6',
brightCyan: '#03c2e6',
blue: '#03c2e6',
white: '#d0d0d0',
brightBlack: '#808080',
brightWhite: '#ffffff',
}
const darkTheme = {
...defaultTheme,
foreground: '#fff',
background: '#1e1e25',
cursor: '#A0AEC0',
selection: '#ffffff40',
magenta: '#e83030',
brightMagenta: '#e83030',
}
export default defineComponent({
components: {
ResizeObserver,
},
props: {
logs: {
type: Array as PropType<{ type: 'stdout' | 'stderr', text: string }[]>,
required: true,
},
},
setup (props) {
const listeners: IDisposable[] = []
// Theme
const { settings } = useSettings()
const currentTheme = computed(() => {
if (settings.value.darkMode) {
return darkTheme
}
return defaultTheme
})
watch(currentTheme, value => {
if (term) {
term.options.theme = value
}
})
// XTerminal
const fitAddon = new FitAddon()
const searchAddon = new SearchAddon()
let term: Terminal
const el = ref(null)
const xtermTarget = ref<HTMLElement | null>(null)
function createTerminal () {
if (!term && xtermTarget.value) {
term = new Terminal({
theme: currentTheme.value,
scrollback: 4000,
windowsMode: isWindows,
macOptionIsMeta: true,
fontSize: 12,
disableStdin: true,
convertEol: true,
fontFamily: 'Fira Code',
})
// Addons
term.loadAddon(fitAddon)
term.loadAddon(searchAddon)
term.loadAddon(new WebLinksAddon(undefined, undefined, true))
term.open(xtermTarget.value)
if (isWebgl2Supported()) {
term.loadAddon(new WebglAddon())
}
// Scroll
// listeners.push(term.onScroll(position => {
// cached.scroll = position
// }))
}
// Init
fitAddon.fit()
term.focus()
// Initial size is undefined on the server
term.options.theme = currentTheme.value
// https://github.com/xtermjs/xterm.js/issues/291
// term.scrollToLine(cached.scroll)
// term._core._onScroll.fire(cached.scroll)
for (const log of props.logs) {
term.writeln(log.text)
}
}
onMounted(() => {
createTerminal()
})
onActivated(() => {
createTerminal()
})
onUnmounted(() => {
for (const off of listeners) {
off.dispose()
}
listeners.length = 0
})
// Element resize
function onElResize () {
if (term) {
fitAddon.fit()
}
}
watch(() => props.logs, value => {
term.clear()
for (const log of props.logs) {
term.writeln(log.text)
}
})
return {
el,
xtermTarget,
onElResize,
}
},
})
</script>

<template>
<div
ref="el"
class="relative p-2 overflow-hidden"
>
<div
ref="xtermTarget"
class="w-full h-full"
/>

<resize-observer @notify="onElResize()" />
</div>
</template>
2 changes: 1 addition & 1 deletion packages/peeky-client/src/features/test/TestItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ mutation openInEditor ($id: ID!, $line: Int!, $col: Int!) {
<template>
<router-link
:to="{
name: 'test',
name: $route.name.startsWith('test') ? $route.name : 'test',
params: {
suiteSlug: suite.slug,
testSlug: test.slug,
Expand Down
62 changes: 62 additions & 0 deletions packages/peeky-client/src/features/test/TestOutput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

<script lang="ts" setup>
import { useQuery, useResult } from '@vue/apollo-composable'
import gql from 'graphql-tag'
import { useRoute } from 'vue-router'
import { defineProps, onMounted, ref } from 'vue'
import Terminal from '../terminal/Terminal.vue'
const route = useRoute()
const props = defineProps({
test: {
type: Object,
required: true,
},
suite: {
type: Object,
required: true,
},
})
const { result } = useQuery(() => gql`
query testLogs ($runId: ID!, $suiteId: ID!, $testId: ID!) {
run (id: $runId) {
id
testSuite: testSuiteById (id: $suiteId) {
id
test: testById (id: $testId) {
id
logs {
type,
text
}
}
}
}
}
`, () => ({
runId: route.params.runId,
suiteId: props.suite.id,
testId: props.test.id,
}), {
fetchPolicy: 'no-cache',
})
const logs = useResult(result, [], data => data.run.testSuite.test.logs)
const fontsLoaded = ref(false)
onMounted(async () => {
await document.fonts.load('14px "Fira Code"')
fontsLoaded.value = true
})
</script>

<template>
<Terminal
v-if="fontsLoaded"
:logs="logs"
class="w-full h-full"
/>
</template>
15 changes: 15 additions & 0 deletions packages/peeky-client/src/features/test/TestView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import gql from 'graphql-tag'
import { useRoute } from 'vue-router'
import { EditIcon } from '@zhuowenli/vue-feather-icons'
import BaseButton from '../BaseButton.vue'
import BaseTab from '../BaseTab.vue'
import StatusIcon from '../StatusIcon.vue'
import Duration from '../Duration.vue'
import TestFileItem from '../test-file/TestFileItem.vue'
Expand Down Expand Up @@ -136,6 +137,20 @@ mutation openInEditor ($id: ID!, $line: Int!, $col: Int!) {
</div>
</div>

<!-- Tabs -->
<nav>
<BaseTab
:to="{ name: 'test' }"
>
Result
</BaseTab>
<BaseTab
:to="{ name: 'test-output' }"
>
Output
</BaseTab>
</nav>

<router-view
v-if="test"
:test="test"
Expand Down
8 changes: 6 additions & 2 deletions packages/peeky-client/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Dashboard from './features/Dashboard.vue'
import RunView from './features/run/RunView.vue'
import TestView from './features/test/TestView.vue'
import TestViewPlaceholder from './features/test/TestViewPlaceholder.vue'
import TestResult from './features/test/TestResult.vue'

export const router = createRouter({
history: createWebHistory(),
Expand Down Expand Up @@ -33,7 +32,12 @@ export const router = createRouter({
{
path: '',
name: 'test',
component: TestResult,
component: () => import('./features/test/TestResult.vue'),
},
{
path: 'output',
name: 'test-output',
component: () => import('./features/test/TestOutput.vue'),
},
],
},
Expand Down
7 changes: 7 additions & 0 deletions packages/peeky-client/src/style/index.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
@tailwind base;
@tailwind components;

@font-face {
font-family: 'Fira Code';
src: url('/fonts/FiraCode-Regular.ttf');
font-weight: normal;
font-style: normal;
}

html {
@apply text-sm;

Expand Down
Loading

0 comments on commit d787ef5

Please sign in to comment.