Merge pull request #1471 from KoHcoJlb/interface_and_enum_types

graphql: show enums and interfaces
This commit is contained in:
Andrew Bastin
2021-02-08 21:18:24 -05:00
committed by GitHub
6 changed files with 238 additions and 11 deletions

View File

@@ -1,6 +1,7 @@
import type from "../type"
import { shallowMount } from "@vue/test-utils"
import {GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLObjectType} from "graphql"
const gqlType = {
name: "TestType",
@@ -13,8 +14,8 @@ const factory = (props) =>
mocks: {
$t: (text) => text,
},
propsData: props,
stubs: ["field"],
propsData: { gqlTypes: [], ...props },
stubs: ["field", "typelink"],
})
describe("type", () => {
@@ -72,4 +73,162 @@ describe("type", () => {
expect(wrapper.findAll("field-stub").length).toEqual(2)
})
test("prepends 'input' to type name for Input Types", () => {
const testType = new GraphQLInputObjectType({
name: "TestType",
fields: {}
})
const wrapper = factory({
gqlType: testType
})
expect(wrapper.find(".type-title").text().startsWith("input")).toEqual(true)
})
test("prepends 'interface' to type name for Interface Types", () => {
const testType = new GraphQLInterfaceType({
name: "TestType",
fields: {}
})
const wrapper = factory({
gqlType: testType
})
expect(wrapper.find(".type-title").text().startsWith("interface")).toEqual(true)
})
test("prepends 'enum' to type name for Enum Types", () => {
const testType = new GraphQLEnumType({
name: "TestType",
values: {}
})
const wrapper = factory({
gqlType: testType
})
expect(wrapper.find(".type-title").text().startsWith("enum")).toEqual(true)
})
test("'interfaces' computed property returns all the related interfaces", () => {
const testInterfaceA = new GraphQLInterfaceType({
name: "TestInterfaceA",
fields: {}
})
const testInterfaceB = new GraphQLInterfaceType({
name: "TestInterfaceB",
fields: {}
})
const type = new GraphQLObjectType({
name: "TestType",
interfaces: [testInterfaceA, testInterfaceB],
fields: {}
})
const wrapper = factory({
gqlType: type
})
expect(wrapper.vm.interfaces).toEqual(expect.arrayContaining([testInterfaceA, testInterfaceB]))
})
test("'interfaces' computed property returns an empty array if there are no interfaces", () => {
const type = new GraphQLObjectType({
name: "TestType",
fields: {}
})
const wrapper = factory({
gqlType: type
})
expect(wrapper.vm.interfaces).toEqual([])
})
test("'interfaces' computed property returns an empty array if the type is an enum", () => {
const type = new GraphQLEnumType({
name: "TestType",
values: {}
})
const wrapper = factory({
gqlType: type
})
expect(wrapper.vm.interfaces).toEqual([])
})
test("'children' computed property returns all the types implementing an interface", () => {
const testInterface = new GraphQLInterfaceType({
name: "TestInterface",
fields: {}
})
const typeA = new GraphQLObjectType({
name: "TypeA",
interfaces: [testInterface],
fields: {}
})
const typeB = new GraphQLObjectType({
name: "TypeB",
interfaces: [testInterface],
fields: {}
})
const wrapper = factory({
gqlType: testInterface,
gqlTypes: [testInterface, typeA, typeB]
})
expect(wrapper.vm.children).toEqual(expect.arrayContaining([typeA, typeB]))
})
test("'children' computed property returns an empty array if there are no types implementing the interface", () => {
const testInterface = new GraphQLInterfaceType({
name: "TestInterface",
fields: {}
})
const typeA = new GraphQLObjectType({
name: "TypeA",
fields: {}
})
const typeB = new GraphQLObjectType({
name: "TypeB",
fields: {}
})
const wrapper = factory({
gqlType: testInterface,
gqlTypes: [testInterface, typeA, typeB]
})
expect(wrapper.vm.children).toEqual([])
})
test("'children' computed property returns an empty array if the type is an enum", () => {
const testInterface = new GraphQLInterfaceType({
name: "TestInterface",
fields: {}
})
const testType = new GraphQLEnumType({
name: "TestEnum",
values: {}
})
const wrapper = factory({
gqlType: testType,
gqlTypes: [testInterface, testType]
})
expect(wrapper.vm.children).toEqual([])
})
})

View File

@@ -1,5 +1,6 @@
import typelink from "../typelink"
import { shallowMount } from "@vue/test-utils"
import {GraphQLInt} from "graphql"
const factory = (props) =>
shallowMount(typelink, {
@@ -33,6 +34,19 @@ describe("typelink", () => {
expect(callback).toHaveBeenCalledTimes(1)
})
test("jumpToType callback is not called if the root type is a scalar", async () => {
const callback = jest.fn()
const wrapper = factory({
gqlType: GraphQLInt,
jumpTypeCallback: callback
})
await wrapper.trigger("click")
expect(callback).not.toHaveBeenCalled()
})
test("link text is the type string", () => {
const wrapper = factory({
gqlType,

View File

@@ -2,11 +2,25 @@
<div :id="`type_${gqlType.name}`" class="p-2 m-2">
<div class="font-bold type-title" :class="{ 'type-highlighted': isHighlighted }">
<span v-if="isInput" class="text-acColor font-normal">input </span>
<span v-else-if="isInterface" class="text-acColor font-normal">interface </span>
<span v-else-if="isEnum" class="text-acColor font-normal">enum </span>
{{ gqlType.name }}
</div>
<div class="mt-2 text-fgLightColor type-desc" v-if="gqlType.description">
{{ gqlType.description }}
</div>
<div v-if="interfaces.length > 0" class="mb-2">
<h5>{{ $t("interfaces") }}</h5>
<div v-for="gqlInterface in interfaces" :key="gqlInterface.name" class="m-2 ml-4">
<typelink :gqlType="gqlInterface" :jumpTypeCallback="jumpTypeCallback" />
</div>
</div>
<div v-if="children.length > 0" class="mb-2">
<h5>{{ $t("children") }}</h5>
<div v-for="child in children" :key="child.name" class="m-2 ml-4">
<typelink :gqlType="child" :jumpTypeCallback="jumpTypeCallback" />
</div>
</div>
<div v-if="gqlType.getFields">
<h5>{{ $t("fields") }}</h5>
<div v-for="field in gqlType.getFields()" :key="field.name">
@@ -17,6 +31,10 @@
/>
</div>
</div>
<div v-if="isEnum">
<h5>{{ $t("values") }}</h5>
<div :key="value.name" v-for="value in gqlType.getValues()" class="m-4" v-text="value.name" />
</div>
</div>
</template>
@@ -27,11 +45,12 @@
</style>
<script>
import { GraphQLInputObjectType } from "graphql"
import { GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType } from "graphql"
export default {
props: {
gqlType: {},
gqlTypes: Array,
jumpTypeCallback: Function,
isHighlighted: { type: Boolean, default: false },
highlightedFields: { type: Array, default: () => [] },
@@ -45,6 +64,20 @@ export default {
isInput() {
return this.gqlType instanceof GraphQLInputObjectType
},
isInterface() {
return this.gqlType instanceof GraphQLInterfaceType
},
isEnum() {
return this.gqlType instanceof GraphQLEnumType
},
interfaces() {
return (this.gqlType.getInterfaces && this.gqlType.getInterfaces()) || []
},
children() {
return this.gqlTypes.filter(
(type) => type.getInterfaces && type.getInterfaces().includes(this.gqlType)
)
},
},
}
</script>

View File

@@ -1,10 +1,16 @@
<template>
<span class="font-mono font-normal cursor-pointer text-acColor" @click="jumpToType">
<span
:class="{ 'cursor-pointer': !isScalar }"
class="font-mono font-normal text-acColor"
@click="jumpToType"
>
{{ typeString }}
</span>
</template>
<script>
import { GraphQLScalarType } from "graphql"
export default {
props: {
gqlType: null,
@@ -16,12 +22,21 @@ export default {
typeString() {
return this.gqlType.toString()
},
isScalar() {
return this.resolveRootType(this.gqlType) instanceof GraphQLScalarType
},
},
methods: {
jumpToType() {
if (this.isScalar) return
this.jumpTypeCallback(this.gqlType)
},
resolveRootType(type) {
let t = type
while (t.ofType != null) t = t.ofType
return t
},
},
}
</script>

View File

@@ -199,7 +199,10 @@
"docs": "Docs",
"reset_default": "Reset to default",
"fields": "FIELDS",
"interfaces": "INTERFACES",
"children": "CHILDREN",
"deprecated": "DEPRECATED",
"values": "VALUES",
"add_one_header": "(add at least one header)",
"add_one_parameter": "(add at least one parameter)",
"header_count": "header {count}",

View File

@@ -364,6 +364,7 @@
<div v-for="type in filteredGraphqlTypes" :key="type.name">
<type
:gqlType="type"
:gqlTypes="graphqlTypes"
:isHighlighted="isGqlTypeHighlighted({ gqlType: type })"
:highlightedFields="getGqlTypeHighlightedFields({ gqlType: type })"
:jumpTypeCallback="handleJumpToType"
@@ -740,14 +741,17 @@ export default {
? schema.getSubscriptionType().name
: ""
for (const type in typeMap) {
for (const typeName in typeMap) {
let type = typeMap[typeName]
if (
!typeMap[type].name.startsWith("__") &&
![queryTypeName, mutationTypeName, subscriptionTypeName].includes(typeMap[type].name) &&
(typeMap[type] instanceof gql.GraphQLObjectType ||
typeMap[type] instanceof gql.GraphQLInputObjectType)
!type.name.startsWith("__") &&
![queryTypeName, mutationTypeName, subscriptionTypeName].includes(type.name) &&
(type instanceof gql.GraphQLObjectType ||
type instanceof gql.GraphQLInputObjectType ||
type instanceof gql.GraphQLEnumType ||
type instanceof gql.GraphQLInterfaceType)
) {
types.push(typeMap[type])
types.push(type)
}
}
this.graphqlTypes = types
@@ -828,7 +832,6 @@ export default {
}
this.$nuxt.$loading.finish()
},
async getSchema() {
const startTime = Date.now()